前言

掌握细节并不难,难的是如何运用之妙!

词法“陷阱”

词:单词
符号=作为赋值运算,是因为操作频繁,书写简单

词法分析中的“贪心法”

  1. a---b与表达式a -- - b的含义相同,而与a - -- b的含义不同
  2. y = x/*p与y = x / *p不同(第一个/*被理解为注释符)

理解:这也许就是编码规范要求操作符两侧添加合理空格的原因之一吧

字符与字符串

  1. 用双引号引起的字符串,代表的却是一个指向无名数组起始字符的指针。
  2. 字符’\0’对应的十进制是0

语法“陷阱”

运算符优先级

括号(),数组下标[],函数调用,结构成员选择操作符->/. > 单目运算符 > 算数运算符 > 移位运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符 > 条件运算符(三目运算符)

  1. *p++,先计算p++
  2. while (c=getc(in) != EOF)和while ((c=getc(in)) != EOF)

switch语句

  1. case语句结束时不加break的场景,最好加上注释// fall-through

练习题:

  1. 为什么C语言允许初始化列表中出现多余的逗号,如:
int a[] = {1, 2,
};enum {a,b,
};

据说是为了简化代码生成工具的逻辑处理。

语义“陷阱”

指针与数组

C语言中的数组值得注意的地方有以下两点:

  1. C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来。然而,C语言中数组的元素可以是任何类型的对象,当然也可以是另外一个数组。这样,要“仿真”出一个多维数组就不是一件难事。
    (备注:C99标准允许变长数组(VLA))
  2. 对于一个数组,我们只能够做两件事:确定该数组的大小,以及获得指向该数组下标为0的元素的指针。其他有关数组的操作,哪怕它们乍看上去是以数组下标进行运算的,实际上都是通过指针进行的。换句话说,任何一个数组下标运算都等同于一个对应的指针运算,因此我们完全可以依据指针行为定义数组下标的行为。

边界计算与不对称边界

“栏杆错误”,也常被称为”差一错误“(off-by-one error)
问题:100英尺长的围栏每隔10英尺需要一根支撑用的栏杆,一共需要多少根栏杆?
解决方法:用第一个入界点和第一个出界点来表示一个数值范围,即[a, b)
如:x>=16且x<=37,表示成:x>=16且x<38

这个问题,还会出现在:队列,栈等缓冲区操作中

ANSI C标准明确允许这种用法:数组中实际不存在的”溢界“元素的地址位于数组所占内存之后,这个地址可以用于进行赋值和比较。
如:

#define N 1024
static char buffer[N];
static char *bufptr;bufptr = buffer;
……
if (bufptr == &buffer[N])

求值顺序

C语言中只有四个运算符(&&、||、?: 和,)存在规定的求值顺序。

  1. 运算符&&和||首先对左侧操作数求值,只在需要时才对右侧操作数求值
  2. 运算符?:有三个操作数:在a?b:c中,操作数a首先被求值,根据a的值再求操作数b或c的值
  3. 逗号运算符,首先对左侧操作数求值,然后该值被”丢弃“,再对右侧操作数求值。

整数溢出

”溢出“,是指有符号整数算术运算中发生的现象

预处理器

不能忽视宏定义中的空格

#define f (x)  ((x)-1)
#define f(x)   ((x)-1)

宏并不是函数

  1. #define max(a,b) ((a)>(b) ? (a) : (b)),每个参数用括号括起来,整个结果表达式也用括号括起来
  2. max(a++, b),参数不能出现副作用,a会被计算两次
  3. 避免宏展开成庞大的表达式,如:max(a,max(b,max(c,d)))

宏并不是语句

assert宏

  1. #define assert(e) if (!e) assert_error(__FILE__, __LINE__)

例子:

if (x > 0 && y > 0)assert(x > y);
elseassert(y > x);

展开后:

if (x > 0 && y > 0)if (!(x > y)) assert_error("foo.c", 37);
elseif (!(y > x)) assert_error("foo.c", 39);

问题:出现了if和else重新组队。

  1. #define assert(e) { if (!e) assert_error(__FILE__, __LINE__); }

展开后:

if (x > 0 && y > 0){ if (!(x > y)) assert_error("foo.c", 37) };
else{ if (!(y > x)) assert_error("foo.c", 39) };

问题:else之前的分号是一个语法错误

  1. #define assert(e) ((void)((e) || assert_error(__FILE__, __LINE__)))

展开后:

if (x > 0 && y > 0)((void)((x > y)) || assert_error("foo.c", 37)));
else((void)((y > x)) || assert_error("foo.c", 39)));

理解

  1. if, else代码块加{},所以代码规范一般要求,即便只有一条语句的代码块也要加{}
  2. 根据宏具体作用定义形式,有的需要定义成表达式,有的需要定义成语句块(用do{ } while(0)包裹宏内容)

宏并不是类型

#define T1 struct foo *
typedef struct foo * T2;T1 a, b;  // 扩展为:struct foo * a, b; 一个是指针变量,一个是结构体变量
T2 a, b;

C陷阱与缺陷(学习笔记)相关推荐

  1. C陷阱与缺陷学习笔记

    导读 程序是由符号(token)序列所组成的,将程序分解成符号的过程,成为"词法分析". 符号构成更大的单元--语句和声明,语法细节最终决定了语义. 词法陷阱 符号(token)指 ...

  2. C陷阱与缺陷阅读笔记(上)

    词法陷阱 1.贪心法 C编译器对C语言符号的识别,基于每一个符号应该包含尽可能多的字符原则. 如果输入流截止至某个字符之前都已经分解成为一个个符号,那么下一个符号将包括从该字符之后可能组成一个符号的最 ...

  3. C陷阱与缺陷阅读笔记(下)

    关于printf函数 printf函数将数据写到标准输出,fprintf函数将数据写到任何文件,sprintf函数格式化字符串.这三个函数的返回值都是已经处理的字符数. sprintf函数作为输出数据 ...

  4. 《C语言陷阱和缺陷》笔记

    原著:Andrew Koenig - AT&T Bell Laboratories Murray Hill, New Jersey 07094 翻译:lover_P 修订:CQBOY 来自:h ...

  5. C陷阱与缺陷学习总结

    总结序言: 学习大牛们的书籍,每次都是新潮澎湃,热血沸腾,看他们的书心中就有一种信任,有一种无比的膜拜.最初看到这本书的时候心情也是如此.Andy这个大牛我想大家也都如雷贯耳了吧.从图书馆找到他的书籍 ...

  6. 《c陷阱与缺陷》笔记--注意边界值

    如果要自己实现一个获取绝对值的函数,应该都没有问题,我这边也自己写了一个: void myabs(int i){if(i>=0){printf("%d\n",i);}else ...

  7. C陷阱与缺陷--读书笔记3 语义“陷阱”

    第三章 一.知识点 1.C语言中的数组值得注意的地方有以下两点:(P41) (1).C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来.然而,C语言中数组的元素可以是任何类型的对象 ...

  8. 《C陷阱与缺陷》学习笔记

    第一章 词法陷阱 笔记本:<C陷阱与缺陷> 创建时间:2018/4/23 22:06:21                                                  ...

  9. 《C陷阱与缺陷》学习笔记(2):作者有话说

    道阻且长,行则将至.埋头苦干,不鸣则已,一鸣惊人!加油,骚年! 1 参考资料 1.书本参考资料 <C陷阱与缺陷>前言: 2.网络参考资料 [维基百科]Andrew Koenig:https ...

  10. 【2018深信服 醒狮计划】《C陷阱与缺陷》学习笔记

    2018深信服"醒狮计划"笔记 先自我介绍一下,湖大研一计算机的菜鸡,本科网络工程的,大学里不务正业一直在做应用,大一自学过一段时间的MFC,Windows网络编程,感觉比控制台好 ...

最新文章

  1. Crystal For Rubyists 简体中文
  2. python结构嵌套_python2.3嵌套if结构:
  3. 黑莓GPRS上网套餐包月 300MB流量 不限CMWAP/CMNET
  4. 将Python源码编译成pyc和pyo文件
  5. 用php编写比赛评奖系统_php编写的抽奖程序中奖概率算法
  6. activemq和jms_保证主题,JMS规范和ActiveMQ的消息传递
  7. leetcode76 最小覆盖子串
  8. java--面对对象之final关键字和static关键字
  9. sql server 循环_学习SQL:SQL Server循环简介
  10. kafka的延迟操作-pull操作详解
  11. 【Linux】磁盘分区
  12. git pull 提示 There is no tracking information for the current branch
  13. 4ARM-PEG-OH 四臂PEG羟基
  14. python中判断无向图是否有环_数据结构与算法:17 图
  15. 求树的最大宽度(层次遍历法)
  16. 用“掩码位图“,制作类似.png的“透明图片“①
  17. 手机开发|USB调试时未弹出授权对话框
  18. 神器集合!这12个免费工具可以让您的工作更高效
  19. ViewCompanion Premium(HPGL、HPGL2和HP-RTL文件浏览和打印软件)官方正式版V13.10 | 内置viewcompanion注册码
  20. c# 画刻度尺(支持缩放)

热门文章

  1. 绕过cdn探测真实ip方法大全
  2. 5W1H 图书管理系统
  3. 全国省市区表完整版(自己整理)
  4. 台达PLC实现伺服电机的正反转
  5. python 会议室预约系统解决方案_快思聪FUSION会议预约系统
  6. 2020年日历电子版(打印版)_2020全年共12个月的日历表打印版可图片年历-2020年日历A4打印版(每月一张-横版-完美版)下载Word带节假日农历电子版-西西软件下载...
  7. 如何录制Gif动态图片
  8. Android Studio ADB 环境变量配置
  9. 主机安全扫描入门-用Java封装Nmap
  10. vue实例的参数说明