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

                     C陷阱与缺陷

                                                        读书笔记一

1.       词法“陷阱”

1.1   =不同于= =

C语言中用字符更少“=”来表示频繁发生和使用的赋值操作。而用“= =”表示使用相对较少的比较操作。由于两者相近,可能会因为失误将“= =”误输入为“=”。或反之的误输入。造成程序的不正常,增加程序员工作。

解决方法:在做比较时将常量和不可赋值的标识符放在“= =”的左方而将变量放在右侧。这样在出现误将比较写成赋值操作时编译器会提示出错。产生必要的提示信息。(林锐高质量C/C++

1.2   &|”不同于&&||”

&|”表示按位与和或,而“&&||”表示逻辑与和或两者是不同类型的操作,需要注意。

1.3   词法分析的“贪心算法”标识符优先级和结合问题。

编译器在对程序做处理时必然要经历词法分析阶段,在这阶段编译器将拆分语句和标识符使其为下一步做好准备。这时需要注意在程序中是否有容易产生歧义的标识符的组合。

解决办法:尽量用括号将自己需要表达的优先级进行封装。明确结合步骤。

1.4   整型常量:

C语言在区分进制时八进制是以0开头的,十六进制以0x开头。此外在许多编译器中把89也会用作八进制中(标准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是类型,fg是声明符。

其中声明符是在允许范围内可以任意使用括号,并注意优先级结合问题。

既可以用如下方式:

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和声明结构体时注意句尾分号的问题。例如:

ifi==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的问题—ifelse匹配问题。

ifelse的匹配问题是很基础的问题。但是确是很容易出现问题的地方。解决方法只有细心和良好的书写格式。

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 intint型位段,无论有符号或是无符号变型,枚举等。可以使用intunsigned 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];

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

}

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

解决方法:

  1. 尽量减少使用无符号类型数进行计算。以免增加不必要的复杂性,尤其避免因为不存在负值而用其来表示年龄等。
  2. 只用在位段和二进制掩码时,才可以用无符号数,应该在表达式中使用枪支类型转化,使操作数均为有符号数或者无符号数,这样就不必由编译器来选择结果的类型。

2.NULLNUL

       NULL表示指针指向为空,NUL表示字符串的结束标志符。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值