C++/C学习笔记(一)
1.强制转换
(1)基本数据类型转换(值的截断)
例如:double d3=1.25e+20;
double d4=10.25;
int i2=(int)d3;
int i3=(int)d4;
按照浮点数到整数的转换语意,结果应该是截去浮点数的小数部分而保留其整数部分,因此i3会得到10,而i2会溢出,因为d3的整数部分远远超出了一个int所能表示的范围,结果当然不正确。
(2)基本数据类型的指针转换(内存截断)
基本数据类型之间的指针转换一般来说必然会造成内存截断或内存访问范围的扩张,除非两种类型具有相同的字节大小。在32位系统中,int,long,float都具有4字节的空间,虽然不会造成内存截断或内存扩张,但是它们之间的指针转换改变了编译器对指针所指向的内存单元的解释方式,因此结果必然是错误的。
例如:double d5=1000.25;
int *pInt=(int*)&d5;
int i4=100;
double *pDbl=(double *)&i4;
从内存访问角度来说,通过pInt访问它所指向的double类型变量d5是安全的(后面的4字节被截断了,可访问内存范围缩小),但是其值绝对不会是d5的整数部分1000,而是d5开头4字节的内容,并解释为int类型数,这个数是不可预料的;同样,通过pDbl访问int类型变量i4,得到的数据不一定就是100,况且造成了可访问内存的扩张,如果往里面写数据,就会产生运行时错误。
若要使用强制转换,必须同时确保内存访问的安全性和转换结果的安全性。对于基本的类型转换,一定要区分值的截断与内存截断的不同。尽量避免做违反编译器类型安全原则和数据保护原则的事情,例如在有符号数和无符号数之间转换,或者把const对象的地址指派给非const对象指针,等等。
2.逻辑运算
逻辑表达式由逻辑运算符 &&,||,! 和标识符构成。逻辑表达式总是返回TRUE(非0值)或FALSE(0)。
在使用运算符“&&”的表达式中,要尽量把最有可能为FALSE的子表达式放在“&&”的左边;同样在使用运算符“||”的表达式中,要尽量把最有可能为TRUE的子表达式放在“||”的左边。因为C++/C对逻辑表达式的判断采用猝死法:如果“&&”左边的子表达式计算为FALSE,则整个表达式就为FALSE,后面的就不必再计算;如果“||”左边的子表达式为TRUE,则整个表达式就为TRUE,后面的也没必要再计算。这种方法可以提高程序执行的效率。
3.数值比较
(1)布尔变量与零值比较
if(flag) //布尔变量flag为真
if(!flag) //布尔变量flag为假
标准C++规定的bool类型常量和整数,指针之间的转换规则:
False→0,true→1 0→false,任何非零值→true
注:不能将布尔变量flag直接与true或者1,-1,0等进行比较。
(2)整形变量与零值比较
if(value==0)
if(value!=0)
(3)浮点数与零值比较
计算机表示浮点数都有精度限制,超出精度限制,计算机会把精度之外的小数部分截断。本来不想等的浮点数在计算机内就可能相等了。
比较:如果两个同符号浮点数之差的绝对值小于或等于某一个可接受的误差(精度),则认为它们相等。设浮点变量x和y,精度EPSILON=1e-6,
则x与y比较方式应为:
if(abs(x-y)<=EPSILON)//x等于y
if(abs(x-y)<=EPSILON)//x不等于y
x与零值比较方式应为:
if(abs(x)<=EPSILON)//x等于0
if(abs(x)<=EPSILON)//x不等于0
(4)指针变量与零值比较
指针变量的零值是“空值”(NULL),即不指向任何对象。尽管NULL的值与0值相同,但是两者的意义(类型)不同:
#ifdef_cplusplus
#define NULL 0
#else
#define NULL ((void*)0)
#endif
假设指针变量的名字为p,它与零值比较:
if(p==NULL) //p与NULL显示比较,强调p是指针变量
if(p!=NULL)
4.符号常量
(1)#define定义的宏常量
预编译伪指令,它定义的宏常量在进入编译阶段前就已经被替换为所代表的字面常量。
(2)用const定义的常量
在标准C语言中,const符号常量默认外连接,不能修改,会分配存储空间,不能在两个编译单元中同时定义一个同名的const符号常量(重复定义错误),也不能把一个const符号常量定义放在一个头文件中而在多个编译单元同时包含该头文件。如果要在头文件中定义,要加上static关键字。
在标准C++中,const符号常量默认内连接,可以定义在头文件中,当在不同的编译单元同时包含该头文件时,编译器会认为它们是不同的符号常量,因此每个编译单元独立编译时会分别给它们分配存储空间,而在连接时进行常量合并。
const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行静态类型安全检查,预防意外修改,提高程序的健壮性;而对后者只进行字符替换,易产生边际效应。