您的位置:首页 > 其它

C陷阱与缺陷-- 读书笔记一

2010-06-03 16:21 274 查看
C陷阱与缺陷

读书笔记一
1.
词法“陷阱”
1.1
=不同于= =
C语言中用字符更少“=”来表示频繁发生和使用的赋值操作。而用“= =”表示使用相对较少的比较操作。由于两者相近,可能会因为失误将“= =”误输入为“=”。或反之的误输入。造成程序的不正常,增加程序员工作。
解决方法:在做比较时将常量和不可赋值的标识符放在“= =”的左方而将变量放在右侧。这样在出现误将比较写成赋值操作时编译器会提示出错。产生必要的提示信息。(林锐—高质量C/C++)
1.2
“&和|”不同于“&&和||”
“&和|”表示按位与和或,而“&&和||”表示逻辑与和或两者是不同类型的操作,需要注意。
1.3
词法分析的“贪心算法”—标识符优先级和结合问题。
编译器在对程序做处理时必然要经历词法分析阶段,在这阶段编译器将拆分语句和标识符使其为下一步做好准备。这时需要注意在程序中是否有容易产生歧义的标识符的组合。
解决办法:尽量用括号将自己需要表达的优先级进行封装。明确结合步骤。
1.4
整型常量:
C语言在区分进制时八进制是以0开头的,十六进制以0x开头。此外在许多编译器中把8,9也会用作八进制中(标准c中是禁止的)。需要注意类似0128并不代表128而是1*82+2*81+8*80
1.5
字符和字符串:
首先是”和“”的问题,单引号在程序中表示字符即8位整数数值。双引号表示一个指向无名数组起始字符的指针,该数组被双引号之间的字符和以及额外的一个二进制值为0的字符’/0’初始化。举例如下:
char a[]={’a’};
char b[]=”a”;
数组a的内容并不等同于数组b的内容。a的长度是一个字符长度内容是字符a,而数组b是两个字符长度,内容是字符a和字符/0。
另举例如下
printf(“this is a string/n”);

char string[]={‘t’,’h’,’i’,’s’,’ ’,’i’,’s’,’
’,’a’,’ ’,’s’,’t’,’r’,’i’,’n’,’g’,’/n’0};
printf(string);
是等效。所以在用数组存储字符串时注意预留最后的结束符/0的空间。
另:当出现’abc’这是根据编译器的不同做出不同的处理的。所以要注意双引号与单引号的区别。
2.
语法“陷阱”:
2.1
函数声明:
(*(void(*)()0)();这是一个显示的函数调用。如何去分析类似的使用和声明的问题。
任何C变量的声明都由两部分组成:类型以及一类类似表达式的声明符。
最简单的声明符就是单个变量:
float f,g;其中float是类型,f和g是声明符。
其中声明符是在允许范围内可以任意使用括号,并注意优先级结合问题。
既可以用如下方式:
float ((f));表示 ((f))的类型为float,同样f也为float。
同样声明一个函数指针如下:
float (*h)();
h是函数指针。
将开始的(*(void(*)()0)();分解分析如下:首先这是个函数调用,即类似(*h)();
“*(void(*)()0)”等同于*h,是个函数的指针。而“(void(*)()0)”相当于h,“void(*)()”可以认为是类型,即“指向返回值为void的函数指针”用其对0做类型转换,最终(*(void(*)()0)()调用的是从0这个地址开始的函数。
2.2 运算符优先级
C语言有15个优先级,具体如下:
() []
->
.
left to right

! ~ ++ -- +(正号) -(负号) *(指针取值符) (type) sizeof right to left

* /
%
left to right

+
-
left to right

<<
>>
left to right

< <= >
>=
left to right

== !=
left to right

&
left to right

^
left to right

|
left to right

&&
left to right

||
left to right

?:
right to left

= += -= *= /= %= &=
^= |= <<=
>>=
right
to left

,
left to right
15个优先级全部记住需要点时间和熟悉,但是因为优先级的问题造成的程序问题也是不容小看的。例如:
r = high<<4 +low;
程序员本意想将high(值小于16)和low(值小于16),这两个数按高低位组成新的数据r的,但是在上式中由于+的优先级高于<<,就变成了将high左移4+low位然后赋值给r。
所以需要熟悉运算符优先级,找到网上的记忆口诀如下:
记忆口诀:
括号成员第一;
//括号运算符[]() 成员运算符. ->

全体单目第二;
//所有的单目运算符比如++ -- +(正) -(负) 指针运算*&

乘除余三,加减四;
//这个"余"是指取余运算即%

移位五,关系六;
//移位运算符:<< >> ,关系:> < >= <= 等

等于(与)不等排第七;
//即== !=

位与异或和位或;
//这几个都是位运算: 位与(&)异或(^)位或(|)

"三分天下"八九十;

逻辑或跟与;
//逻辑运算符:|| 和 &&

十二和十一;
//注意顺序:优先级(||) 底于 优先级(&&)

条件高于赋值,
//三目运算符优先级排到 13 位只比赋值运算符和","高

逗号运算级最低!
//逗号运算符优先级最低
另外:还有一个简单的解决办法,那就是添加括号。
2.3 句尾的分号问题。
当使用if()语句和return和声明结构体时注意句尾分号的问题。例如:
if(i==0)
return
x= y[20];
本意当i等于零时,结束,但因为少了分号,则变为返回x= y[20];
的值了。
2.4
swith
c语言中将switch中的case当作真正的标号来看待,即case作为程序的入口,从此处开始执行,并且顺执行下去。举例:
switch (num)
{
case 1:
printf(”case 1/n”);
case 2:
printf(”case 2/n”);
case 3:
printf(”case 3/n”);
case 4:
printf(”case 4/n”);
case 5:
printf(”case 5/n”);
}
当num=2时,屏幕输出为:
case 2
case 3
case 4
case 5
当需要对每个分支进行控制时需要使用break;语句。
2.5
函数调用
C语言中规定函数调用即使没有参数也要包括函数列表。仅使用函数仅是计算函数地址而不调用函数。
2.6
“悬挂”else的问题—if与else匹配问题。
if与else的匹配问题是很基础的问题。但是确是很容易出现问题的地方。解决方法只有细心和良好的书写格式。
3.
语义陷阱
3.1
指针和数组。
在C语言中数组和指针俩跟着紧密联系。关于这两者需要注意两点。
1.
C语言中只有一维数组,而且数组的大小必须在编译期就做为一个常数确定下来。(C99新标准中支持可变长数组,gcc编译器实现了可变长数组但与标准有出入)但是数组的成员可以是任意类型,当然也可以是另一个数组,这样就可以构成多维数组。
2.
对于数组,我们仅能确定该数组的大下,以及指向该数组的下标为0的元素指针。即使乍看上去是以数组下标完成,而实际是以指针操作完成。
PS:同时注意C语言对数组的越界操作时默认允许的,在一些编译器的实现上可能会给出告警,但是并影响编译。所以要注意这点。
根据这点我们可以定义出指向数组的指针 int (* ap)[20]
ap是一个指向拥有20个整型变量的数组,即ap+1将移动20个整型变量的距离。
3.2非数组指针。

C专家编程
1.
安静的按转变(编译的类型转换)
char ,short int或int型位段,无论有符号或是无符号变型,枚举等。可以使用int或unsigned int型时,如果int能表示源类型的所有值,则转换为int型,否则转换为unsigned int型。这就是所谓的整型升级。
类型的转换可以简单表述为,当执行算术运算时,如果类型不同就会发生类型转换。数据类型一般朝着个浮点精度更高,长度更长的类型转换。整型如果signed不会丢失信息就转化为signed,否则则转化为unsigned型。
举例:
int array[]
={12,56,36,25,1,23,89}

#define TOTAL_ELEMENTS
(sizeof(array)/sizeof(array[0]))

main()

{

int d=-1,x;

/* ……..*/

if(d<= TOTAL_ELEMENTS-2)

x= array[d+1];

/* ……………………*/

}

上例中if(d<=TOTAL_ELEMENTS-2)比较时,发生了类型转换,TOTAL_ELEMENTS为unsigned型则,d也要转化为unsigned,则d在转化后是一个非常大的正数。条件一直成立。一个类型转化引起的bug。
解决方法:

尽量减少使用无符号类型数进行计算。以免增加不必要的复杂性,尤其避免因为不存在负值而用其来表示年龄等。

只用在位段和二进制掩码时,才可以用无符号数,应该在表达式中使用枪支类型转化,使操作数均为有符号数或者无符号数,这样就不必由编译器来选择结果的类型。

2.NULL与NUL。
NULL表示指针指向为空,NUL表示字符串的结束标志符。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: