导读 《C陷阱与缺陷》

本文详细阐述了C语言编程中常见的陷阱与注意事项,包括词法分析、语法使用、语义理解和函数调用等方面的问题。重点讲解了赋值运算符与比较运算符的区别、贪心法在词法分析中的应用、整型常量与字符/字符串的表示、数组与指针的操作、以及函数声明、优先级、分号使用等核心概念。

导读


针对本书的阅读笔记。
此书形成之日,pascal占据天下,所以更多的对关心的是分类器之间的关系与其他不同。

一些术语

token,指的是程序的一个基本组成单元,其作用相当于一个句子中的单词。
词法分析器,编译器中负责将程序分解为一个一个符号的部分。


第一章 词法“陷阱”

1.1=(赋值操作符)不同于==(比较操作符)

1.1.1其一:赋值操作符与比较操作符

目标:赋值操作被嵌入到更大的表达式中的问题

while(c=' '||'\t'||'\n')
  c=getc(f);

对于判断语句来说

  • 前言:等号为赋值语句,是把右面的值赋予c
  • 对于判断语句来说,是看赋值是否成功
  • 该语句会成为一个循环
  • 某些C编译器在发现先形如e1=e2的表达式出现在循环语句的条件判断部分时,会给出警告消息。

更正
对形如
if(x=y)
  foo();
应该写作
if((x=y)!=0)
  foo();

1.1.1.2其二:比较操作符

if((filedesc==open(argv[i],0))<0)
  error();
前言:函数的调用,成功则返回0或正数,失败则返回-1.
如果此段代码是
filedesc=open(argv[i],0))<0
通过比较filedesc是否小于0来检查函数open是否执行成功。
因为比较运算符的结果0或1,永远不可能小于0,所以函数error()将没有机会被调用。

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

见3.8

1.3词法分析中的“贪心法”

词法分析中的问题:是将其作为两个分别的符号对待,还是合起来作为一个符号对待。
  • 编译器将程序分解成福哈的方法是,从左到右一个字符一个字符的读入,
  • 如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;
  • 如果可能,继续读入下一个字符,
  • 重复上述判断,直到杜若的字符组成字符串已不再可能组成一个有意义的符号。

Kernighan与Ritchie对这个方法表示如下:如果(编译器的)输入流截止至某个字符之前都紫荆被分解为一个个符号,那么下一个符号将包括从改字符之后可能组成一个符号的最长字符串。

这点主要是二义性问题
a----b (a-- -b/a- --b)
y= x/*p (注释与指针)

整型常量

这里讲的是八进制以0开始与为了对齐的问题而写的0(046=46)。

字符与字符串

叙述
字符:单引号引起的一个字符实际上代表一个整数,整数值对应于改字符在编译器采用的字符集中的序列值。
printf("Hello world\n");
字符串:双引号引起的字符串,代表的却是一个指向无名数组起始字符的指针,改数组被双引号之间的字符以及一个河外的二进制值为0的字符‘\0’初始化。

char hello[]={'H','e','l','l','o',' ','w','o','r','l','d','\n',0}
printf(hello),

注意
  • 单引号火气的一个字符代表一个整数;双引号括起的一个字符代表一个指针;混用会被编译器检测到类型错误。例如print(‘\n')里面是一个整数,所以错,应该是一个指针。print("\n")


第二章 语法“陷阱”

暂时略过

2.1 理解函数的声明


2.2 运算符的优先级问题


2.3 注意作为语句结束标志的分号


2.4 switch语句


2.5 函数调用


2.6 “悬挂”else引发的问题



第三章 语义“陷阱”

3.1 指针与数组

c语言数组:
  • c语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来。
  • 对于一个数组,我们只能够做两件事:1.确定该数组的大小;2.获得指向该数组下标为0的元素的指针。
  • 数组实际上都是通过指针进行的。换句话说,任何一个数组下表运算都等同于一个对应的指针运算。
一些例子及建议
  • int *ip;      ip是一个指向整数变量的指针
  • int i;            
  • ip = & i;      将整型变量i的地址赋给指针ip(给*ip赋值,就能够改变i)
如果一个指针指向的是数组中的一个元素,那么我们只要给这个指针加1,就能够得到指向该数组中下一个元素的指针。(如果ip指向一个整数,那么ip+1指向的是计算机内存中的下一个整数,不是指向地址的下一个内存位置

如果两个指针指向的是同一个数组中的元素,我们可以把这两个指针相减。
例如 int *q=p+i; 那么我们可以通过q-p而得到i的值。值得注意的是,如果p与q指向的不是同一个数组中的元素,祭祀他们所指向的地址在内存中的位置中号讲个一个数组元素的整倍数,所得的结果仍然是无法保证其正确性。

指针与数组
  • int a[3]; int *p; p =a;数组a中下表为0的元素的地址赋值给p
  • p = &a; 指向数组地址的指针是不存在的。ANSI C中非法。
  • sizeof(a)的结果是整个数组a的大小,而不是指向数组a的元素的指针的大小。
  • *a=84   将a数组标号为0的元素的值设置为84
  • *(a+i) 数组a中下表为i的uansu弟弟引用,简记为a[i],等价于i[a]
二维数组
c语言中没有二维数组实际上是以数组为元素的数组。
int calendar[12][31] 是一个有着12个数组类型元素的数组,他的每个数组类型元素又是一个有着31个整型元素的数组。
sizeof(calendar[4])的结果是31*sizeof(int)
int *p=calendar[4]; 指针p指向了数组calendar【4】中下表为0的元素。
calendar[4]是一个数组,我们当让可以通过下表的形式来制定这个数组中的元素 i=calendar[4][7];
i=*(*(calendar+4)+7)
  • p=calendar; 这个语句是非法的。 calendar是“数组的数组”
  • int (*monthp)[31] 声明一个拥有31个整形元素的数组,monthp是一个指向这样的数组的指针。
  • monthp = calendar;

3.2 非数组的指针


3.3 作为参数的数组声明


3.4 避免“举隅法”

"举隅法"是一种文学修辞方法,“以含义更宽泛的词语来代替含义相对较窄的词语”
混淆指针域指针所指向的数据。

3.5 空指针并非空字符串


3.6 边界计算域不对称边界


3.7 求值顺序


3.8 逻辑运算符&&、||和!


3.9 整数溢出


3.10 为函数main提供返回值


第四章 连接



第五章 库函数



第六章 预处理



第七章 可移植性缺陷



第八章 建议与答案



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值