表达式求值的过程中,往往会伴随着数据类型的自动转换,而数据类型的转换的一般情况如下所示,从低到高。
long double
double
float
unsigned long int
long int
unsigned int
int
有几点需要注意:
1. 这里没有出现char,short,enum,因为它们会被自动转换为int或是unsigned int类型
2. 当unsigned int 和 int 出现在同一个式子中时,需要注意,如:5u – 10,结果是大于0的,不是-5,而是4294967291
3. 另外对于32位机,由于long int的表现形式与int相同,unsigned int的表现形式与unsigned long int相同,因此long int的数值范围无法包含unsigned int,因此在32位机进行处理时,它们间的类型的自动转换可以理解为4位的有符号自动转为4位的无符号,然后再向上转为float型等;如 unsigned int a = 45; long int b = 2147483643; a + b中可以理解为都转换成了unsigned long类型
摘要:就windows下傻瓜式安装codelite时出现的一些问题进行了阐述,并提供了一定的解决方案,希望对于遇到相同情况的读者有所帮助。
在这里不说为什么选择codelite这么个IDE,其实这样的争执在几分钟后往往会偏离正题,思绪被情绪控制,各执己见,何谓?
什么是傻瓜式安装呢,就是在windows下,利用release的安装版本,这里下的是包含wxWidget,MinGW的安装版本,然后根据它的指示一直下一步下一步进行就行了。如果你的机子最初是干干净净的,最后安装的程序会是成功的。但是这基本上是不可能的,而我呢,恰好由于事先在机子上装了些东西,就使得安装完成后并不能像codelite官网中quick start所说的那样进行操作。
想原因可能如下:
*. 原来安装过code::block,将code::block自带的mingw32-make.exe所在的目录加在了环境变量中,并且原来安装过库wxWidget的低版本,由于选择了codelite,于是将wxWidget原来的版本和code::block都删除了。并在环境变量中也将值删除了。
然后卸载掉codelite进行重装,不过依然不能运行wxWidget框架程序。build时出现需要将“WXWIN = C:\wxWidget”的提示,查看了一下setting:tags setting : ctags : include files内包含的竟然是perl中的mingw,额装的东西太多了,于是将perl相关的环境变量也删除了。
继续重装,结果发现依然没有任何改变,悲剧,到底是怎么回事呢,细想来,原来C盘的Application Data中有codelite的配置文件,由于卸载的过程中并没有将这些配置文件删除,重装后依然使用的是这些配置文件,才导致了问题的出现,于是将Application Data下的codelite文件夹删除,再进行重装,终于是成功了。
傻瓜式安装,只是设想到了理想的情况下如何进行处理,没有料想到会出现想我这样的情况,所以安装结束后相关配置文件中的数据的设定出现了错误,导致无法正常build。
至此,安装结束了,学习才刚刚开始。
也许你会说,为什么不从codelite软件中对配置数据进行设定呢,其实进行了尝试,正确的设定了ctags : include files的位置,但是结果依然无法build,出现类似“WXWIN = C:\wxWidget”的提示,于是将其设置到环境变量中也没有得到相应的解决,所以茫然之下才出此下策,一切从头开始。
相信随着深入的学习,对于它的处理方式,配置文件的设定等,都会慢慢了解的,一鸣惊人的背后蕴含着多少的厚积呢,急不来的。
补充一下:
环境变量的设置不是设置计算机的环境变量,而是设置codelite下setting下的环境变量,额,因为惯性思维没有考虑到这点,悲剧哦。
VIM的学习不是一天两天的事情,先用着IDE吧,并逐步学习VIM的使用。
最近由于工作的需要,要用到界面的开发,选什么库呢,MFC,Qt or wxWidget。对微软的东西有种莫名的反感,也许那么多年了不得不用它的产品,特别是操作系统,有时特别是在安装程序的时候,点着是否接受协议,不接受自然是无法安装了,都习惯性点击同意了,会刹那的感觉自己是不是被当作了一个“低能儿”的,也许是这么一种心理的作怪,总是在矛盾中用着微软的产品,想跳出使用linux,但是学习上,别人都是用office进行操作,导师对于工作的检查也都用MS office,换用linux的话会使工作进度,交流等产生一定的障碍吧。很多东西就仿佛和它绑在了一起。慢慢来,一步一步将它对自己的影响逐渐降低吧,又从开源的角度考虑,选择了wxWidget于是又选择了codelite作为学习该库的IDE了。自然VIM依然在不断的进行中。
相信微软是无法束缚我的思维的。成为工具的主人。
总结:
随着对VIM的慢慢接触,逐渐发现了它作为编辑器的强大,相信自己会持之以恒的。
习惯有时能够在解决问题上节省很多的时间,但会使得一个人的敏感性降低,没有什么东西是一尘不变的,正因为人们都在重复着一种类似的生活方式,上班,工作,回家,睡觉,继而上班,这样不断的往复着,或是学习上课等,也是以一种类似的方式往复着,这样便更加容易陷入到习惯的河流中,慢慢迷失了原有的激情,原有的对于事、对于人的敏感性。在这里并不是想要一味着贬低习惯,只是想唤醒人们心中那种不知道出现了多久的迷茫,唤醒覆灭已久的热情,让他们能够在生活的脚步中,保持一颗清醒的头脑,成为习惯的主人,而不是成为习惯的奴隶,并谨记流动着的才是真理,死寂不变的是一滩死水,今天的清澈,并不能改变未来的肮脏与浑浊。
写给自己,也写给大众。
摘要:简要比较了直接、指针、引用三中函数参数传递方式,并对其优缺点进行了大致的说明
1. 众所周知,函数参数的直接传递就是实现一个拷贝值,这个拷贝值的变化,并不会改变原值的变化,因为两个被来就是不同的个体,就好比两个克隆的个体,虽然两者之间有很多相同的地方,但是它们的思维是独立的。
2. 通过指针的方式给函数参数传值呢,从根本上来讲,它仍然是直接传值,但是这个拷贝的值比较特殊,是一个地址罢了,如:
1: void fun(int *pnValue)
2: {
3: ...;
4: }
5: int main()
6: {
7: int nValue = 6;
8: fun(&nValue);
9: }
它其实是将nValue的地址拷贝给了pnValue,如果在fun函数中,一开始就将pnValue的值给改变,如pnValue=&nValue2,那么pnValue指向的内容的改变,将不会影响到nValue。
同时有一点需要注意的是,空指针对象是没有意义的,会引起程序的奔溃,因此在开始因该进行检测,if(!pnValue) return;
通过指针传参数的优点:
1) 允许你改变传递的参数的值
2) 由于它拷贝的仅仅是一个字节大小的地址,因此传递的过程是快速的,特别对于较大的结构或是类
3) 我们通过这种传参方式,获取从函数中返回的多个量
缺点:
1) 传递的只能是普通的变量,不能是字面常量或表达式
2) 所有的传递值都得检查是不是空指针
3. 通过引用的方式传递参数
虽然在底层的操作中,通过引用也是通过“指针的方式”进行实现的http://blog.csdn.net/wanwenweifly4/article/details/6739687),但是从语言上考虑,引用是对象的别名,也是一个对象,并不是指针,因为它的概念是在语言上定义的,而不是底层的实现方式,换一种思维,抛开所有的比汇编语言高级的语言,回到汇编语言建立初期,单单从汇编语言上考虑,那时有没有指针的概念呢?因此应该理性的对待引用在C++语言中的概念,也应该冷静的认识它在底层中的实现方式,区分的对待,其实也不用争执于这个问题,认清了,会用了,就成了。(这些仅个人见解,批判的看待吧)。
它在传参中的优点:
1) 允许你改变传递的参数的值
2) 传递的过程是快速的,特别对于较大的结构或是类
3) 可以通过添加一个const,避免不经意的改变
4) 我们通过这种传参方式,获取从函数中返回的多个量
缺点:
1) 非const类型的参数,传递的只能是普通的变量,不能是字面常量或表达式
2) 不容易区分哪些变量是input,需要output,或都是
3) 通过函数的调用,很难看出那个参数是将被改变的,因为它和直接传值的方式相同,只能通过函数原型进行辨认,当程序员不小心忽视的时候,可能会导致错误的发生
ps:
1: #include <iostream>
2:
3: int nFive = 5;
4: int nSix = 6;
8: void SetToSix(int *pTempPtr);
9:
10: int main()
11: {
12: using namespace std;
13:
16: int *pPtr = &nFive;
19: cout << *pPtr;
20:
23: SetToSix(pPtr);
27: cout << *pPtr;
28:
29: return 0;
30: }
31:
33: void SetToSix(int *pTempPtr)
34: {
35: using namespace std;
36:
38: pTempPtr = &nSix;
41: cout << *pTempPtr;
42: }
上面这个程序中输出的结果是 565
如果想使得输出的结果为566呢,有一个方法可以实现,就是采用指针的引用,如下:
1: // pTempPtr is now a reference to a pointer to pPtr!
2: // This means if we change pTempPtr, we change pPtr!
3: void SetToSix(int *&pTempPtr)
4: {
5: using namespace std;
6:
7: pTempPtr = &nSix;
8:
9: // This will print 6
10: cout << *pTempPtr;
11: }
1. const指针总是指向相同的地址,该地址是不能被改变的.
1: int nValue = 5;
2: int *const pnPtr = &nValue;
*pnPtr = 6; 这样的操作是可行的;而 int nValue2 = 2; pnPtr = &nValue2;这样的操作是不可行的。
int *const pnPtr 可以这么理解,pnPtr当作地址,该指针有const地址,并且指向一个整型变量。
2. 指向const变量(虽然这个变量本身可以不是const的)的指针
1: int nValue = 5;
2: const int *pnPtr = &nValue;
const int *pnPtr可以这么理解,一个可以改变地址的指针指向的是一个不能改变大小的变量。
也就是说,*pnPtr = 6;这样是不行的;int nValue2 = 6; pnPtr = &nValue2;这样是可行的
3. 当然,还有一种就是两者都不能改变了。
int nValue = 2;
const int *const pnPtr = &nValue;
不过此时可以通过nValue=6;来改变*pnPtr的值。把int nValue = 2;改成 const int nValue = 2;那么就什么都不能变咯。
摘要:举了几个动态内存分配过程中,发生内存泄漏的例子
1. 分配了内存,却没有及时删除,导致泄漏
1: void doSomething()
2: {
3: int *pnValue = new int;
4: }
2. 为指针变量分配了一个内存,然后又让指针变量指向其他的值,导致泄漏
1: int nValue = 5;
2: int *pnValue = new int;
3: pnValue = &nValue; // old address lost, memory leak results
3. 连续分配了两次内存,第一次分配的内存由于没有delete,导致泄漏
1: int *pnValue = new int;
2: pnValue = new int; // old address lost, memory leak results
先来看一下一段代码:
1: char szString[255];
2: cin >> szString;
3: cout << “You entered: ”<< szString << endl;
这段代码在c++程序中是很常见的,但仔细想一下如果你输入的字符串长度大于255,就会出现溢出的现象,也许这个问题通常会被人忽略。
其实我们可以选择一个更好的实现方法,如下
1: char szString[255];
2: cin.getline(szString, 255);
3: cout << "Your entered: " << szString << endl;
通过这样的方式,像第一个例子中可能出现的内存溢出的现象就可以避免了。
from http://www.learncpp.com/cpp-tutorial/59-random-number-generation/
通常在游戏,统计模型程序和科学模拟中会用到随机事件。
而由于计算机的本质结构决定计算机只能生成伪随机数据。
伪随机生成器,设定一个初始值(seed),对它进行操作形成不同的数值,让它看上去与初始值没有联系。如果算法足够复杂,将同样的算法用到最后生成的数字,这样就能够产生一些列看上去随机的数值。
下面是一个产生100个伪随机数的程序:
1.
1: #include <stdafx.h>
2: #include <iostream>
3: using namespace std;
4:
5: unsigned int PRNG()
6: {
7: // our initial starting seed is 5323
8: static unsigned int nSeed = 5323;
9:
10: // Take the current seed and generate a new value from it
11: // Due to our use of large constants and overflow, it would be
12: // very hard for someone to predict what the next number is
13: // going to be from the previous one.
14: nSeed = (8253729 * nSeed + 2396403);
15:
16: // Take the seed and return a value between 0 and 32767
17: return nSeed % 32767;
18: }
19:
20: int main()
21: {
22: // Print 100 random numbers
23: for (int nCount=0; nCount < 100; ++nCount)
24: {
25: cout << PRNG() << "\t";
26:
27: // If we've printed 5 numbers, start a new column
28: if ((nCount+1) % 5 == 0)
29: cout << endl;
30: }
31: }
事实上我们这个例子并不是很好,但是它足够用来说明伪随机数是如何产生的。
C++产生伪随机数
语言中有内置的随机数生成器,它需要两个独立的函数组合使用。
srand() 用来设置初始seed。srand()通常只调用一次。
rand()生成序列中的下一个随机值。
2.
1: #include <iostream>
2: #include <cstdlib> // for rand() and srand()
3: using namespace std;
4:
5: int main()
6: {
7: srand(5323); // set initial seed value to 5323
8:
9: // Print 100 random numbers
10: for (int nCount=0; nCount < 100; ++nCount)
11: {
12: cout << rand() <<“\t”;
13:
14: // If we've printed 5 numbers, start a new column
15: if ((nCount+1) % 5 == 0)
16: cout << endl;
17: }
18: }
rand()产生的伪随机数的范围在0到RAND_MAX之间,通常它的值在cstdlib中为32767.
通常我们并不想要这个范围内的随机值。我们想要两个nLow和nHigh范围内。如1-6.
3.
1: // Generate a random number between nLow and nHigh (inclusive)
2: unsigned int GetRandomNumber(int nLow, int nHigh)
3: {
4: return (rand() % (nHigh - nLow + 1)) + nLow;
5: }
当我们反复运行第2个程序的时候,发现每次的结果都是相同的。由于我们每次设置的seed都是相同的。因此我们使用了time()函数来产生seed。
1: #include <stdafx.h>
2: #include <iostream>
3: #include <cstdlib> // for rand() and srand()
4: #include <ctime> // for time()
5: using namespace std;
6:
7: int main()
8: {
9:
10: srand(time(0)); // set initial seed value to system clock
11: for (int nCount=0; nCount < 100; ++nCount)
12: {
13: cout << rand() << "\t";
14:
15: if ((nCount+1) % 5 == 0)
16: cout << endl;
17: }
18: }
怎样的伪随机生成器算是好的呢?
1)生成器会以相同的概率产生每一个数值。
2)随机序列的下一个数字不能明显、容易被预测
3)随机数生成器产生的数值有一个好的分布,忽而小,忽而大,让人感觉是随机的。
4)所有的伪随机生成器都是周期性的
rand()函数只是一个普通的伪随机生成器
大多数rand()都是使用Linear Congruential Generator方法实现的。
由于RAND_MAX通常是32767,意味着如果我们想要更大的范围就会不适合了。同时,rand()也不适合产生浮点型随机值如0.0-1.0. 同时rand()相对于其他算法拥有一个短的周期。
可以使用Mersenne Twister,它具有更好的结果,同时使用相对简单。
from http://www.learncpp.com/cpp-tutorial/44-type-conversion-and-casting/
类型隐式转换
类型隐式转换的步骤如下所示:
Long double (highest)
Double
Float
Unsigned long int
Long int
Unsigned int
Int (lowest)
最底层是int而不是short或是char,是因为char和short在求值时会被扩展为int。
隐式转换会出现一些有趣的问题,如5u - 10。也许你会认为结果是-5.但是10为int型,发生隐式转换成unsigned int型,因此最后结果为unsigned int型,2^32 - 5。又如float fValue = 10/4;
from http://www.learncpp.com/cpp-tutorial/38-bitwise-operators/
前面的文章在 http://www.cnblogs.com/grass-and-moon/
位操作符的由来主要是为了更充分的利用内存,如果使用bool型变量,它是一个字节大小,其中一个字节的内存中只有一个位被利用,其余的位都被浪费了。但是现在内存是充裕的,程序员发现往往将代码表达的更加让人理解,更加容易维护是一种更好的选择。
因此,位操作符的使用不再像最初那么平常了,除了一些特殊的环境中,如需要巨型数据处理的科学计算,或需要超速执行的游戏中,仍然会被使用。不管怎么样,对它有个充分的认识和理解还是必须的。
Operator | Symbol | Form | Operation |
left shift | << | x << y | all bits in x shifted left y bits |
right shift | >> | x >> y | all bits in x shifted right y bits |
bitwise NOT | ~ | ~x | all bits in x flipped |
bitwise AND | & | x & y | each bit in x AND each bit in y |
bitwise OR | | | x | y | each bit in x OR each bit in y |
bitwise XOR | ^ | x ^ y | each bit in x XOR each bit in y |
注意 当我们使用位操作符时,使用的变量类型通常是无符号整型。
为操作符的原理通常很简单,但是要将它用活,通常是有一定难度的。