C陷阱与缺陷

简介: C陷阱与缺陷

C陷阱与缺陷和高质量C编程这两本书主要是总结一些C编程人员常犯的一些错误,常遇到的一些语法陷阱,及不良的编程风格。无论是有多年经验的编程老手还是刚入门的新手,细细品读,都会有不少的收获和感悟。此篇博客是个人读完这两本书后的一些感悟及对一些内容的记录,方便日后翻阅。


词法陷阱

  • 多个操作字符组在一起的处理方法

编译器中负责将我们编译的命令符号进行分解的部分称为”词法分析器“,而词法分析器在工作运行时采用的处理策略为”贪心法“ 。什么意思呢?举个栗子,如这段语句a---b,编译器在运行时会识别为a-- - b而不是a - --b 也就是说编译器会先按从左到右的顺序读取一个字符,紧接着再读取第二个字符,然后判断这两个字符是否能够组成一个有实际意义的操作符,如果能组成,那么编译器还会读取第三个字符(如果存在第三的话)然后再进行同样的判断,直到读取的字符与前面所读取的字符的组合没有实际的意义了。我们再把视角拉回到a---b中,编译器会先读取” - “这个操作符,然后再读取一个” - “操作符,发现能够组成” -- “这个有实际意义的操作符,接着编译器再读取第三个” - “操作符,然后发现三个” - “操作符组成的字符没有实际意义,因此将” --- “拆分成了” -- “和” - “,最终语句的效果就是a-- - b ,这里需要注意的是,这是编译器在面临多个操作字符组在一起的处理办法。

  • 操作符之间的空格与括号的合理应用各个操作符之间的空格要根据想表达的意思谨慎添加,比如想表达是否等于的判断,应该写成==,而不是= =,= =这样的格式,严格的编译器是直接报错的。如 m = n/*p 这段语句的本意是n除以p指向的内容,然后把值赋给m,但编译器会将 /* 解读成注释的前半部分,不会再管后面的内容,如果写成m = n / *p 或者m = n / (*p)就能表达出想要表达的意思。
  • 不同的编译器采用的操作符的处理办法不尽相同,所以我们在编写程序时该加空格就加空格,该加括号就加括号,尽量把意思表达清楚,避免写出会产生歧义的代码,别舍不得用括号,不为难编译器,也不要为难自己。

    image.png整型数字运算需要注意的

  • 整型数字在运算时,避免为了使数字对齐美观而在数字面前加上 0 ,这样的话,编译器会将这串十进制数字识别为八进制数字,如果不知道这种情况,会导致得不到想要的结果而苦恼
//例如
int a = 123;
int b = 034;  //这里编译器会把34当作八进制数字来处理

如果在进行整数除法运算,且想得到具体的结果时,最好将其转化成浮点型,使用强制类型转换,或在某个数字后面加上" .0 "编译器会自动将其转换成浮点型

    float c = 0.0f;
    c = 1 / 4;
    printf("%f", c);//不可,c的结果为0.000000
    float c = 0.0f;
    c = (float)1 / 4;
    printf("%f", c);//可,c结果为0.250000
    float c = 0.0f;
    c = 1.0 / 4;
    printf("%f", c);//可,c结果为0.250000

char类型本质上是属于整型家族的,其在运算时,需要进行整形提升,为什么进行整型提升呢?

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度 一般就是int的字节长度,同时也是CPU的通用寄存器的长度。 因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长 度。 通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令 中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。

在存储时要进行截断,因为char只能存放8个二进制数

image.png字符与字符串的区别

由单引号引起的字符,本质上代表着一个整数,该整数值对应着编译器采用的字符集所对应的字符的序列值。由双引号引起的字符串,本质上是一个指针,指向一个无名数组起始字符的指针,该数组被双引号之间的字符以及\0所初始化。

避免在单引号的字符里放入多个字符,因为不同编译器的处理规则是不同的,有的取第一个字符,有的取最后一个字符,所产生的结果都是不确定的。也不要将单引号引起的字符赋值给char 类型的指针,这样是不行的,因为它的本质就是一个整数,但有的编译器不会报错的,这需要我们多加留意了。

char *p = 'a'; //这样不行,'a'不是一个指针;

//由于printf()函数的一些特性,采用不确定参数,原型为 int printf(const char *format[argument]...)

//导致一些编译器对该函数的参数的检查并不是很严格

//这就容易导致一个问题

printf('\n'),这种写法可能不会报错,这会导致难以预料的结果

image.png良好的编程习惯

为了避免产生一些不必要的错误,我们可以从编程习惯上逐步改善

int a = 3;
if ( a == 3 )     不妨将其写成if (3 == a)
{                 3是无法被赋值的,如果少些一个 = 编译器会报错的
 ......
}
//关键字与括号的接触中间最好有一个空格,以表明和函数的区别
if()      //建议写成if ()
while()   建议写成while ()  
//函数是紧贴括号的
    printf()
    scanf()
   // 不过现在一些高级的编译器会自动加空格的,还是很方便的;
 //函数在声明和定义时,最好将变量名补全 。
  void fun(int; int)//不好的风格
  void fun(int x, int y)//良好的风格




目录
相关文章
|
4月前
|
测试技术
常见测试陷阱
常见测试陷阱
|
4月前
|
存储 程序员 编译器
C陷阱与缺陷:语法陷阱
C陷阱与缺陷:语法陷阱
24 0
|
4月前
|
自然语言处理 编译器 程序员
C陷阱与缺陷:词法陷阱
C陷阱与缺陷:词法陷阱
26 0
|
存储 人工智能 自然语言处理
【C缺陷与陷阱】----语义“陷阱”
那获得该下标为0的元素的指针,如果给这个指针加1,就能得到指向该数组中下一个元素的指针。也就是指针+一个整数得到的还是指针,只不过指针的位置发生改变
80 0
|
自然语言处理 编译器 程序员
【C陷阱与缺陷】----语法陷阱
由于一个程序错误可以从不同层面采用不同方式进行考察,而根据程序错误与考察程序的方式之间的相关性,可以将程序错误进行划分为各种陷阱与缺陷
68 0
|
自然语言处理 编译器 程序员
《C陷阱与缺陷》----词法“陷阱”
由于在C语言中赋值操作相对于比较出现更加频繁,所以将字符较少的符号=赋予更常用的含义—赋值操作。
69 0
|
编译器 C语言
源于《C陷阱与缺陷》----研究程序死循环问题
所以最后答案应该就是打印了12次xiao tao,然后越界访问出现错误,使arr[10]=0,arr[11]=0了 但最后答案却不是这样。
82 0
|
自然语言处理 编译器 C语言
|
自然语言处理 算法 编译器