《C陷阱与缺陷》读书笔记

目录

  • 《C陷阱与缺陷》读书笔记
  • 一,函数指针和指针函数
  • 二,C语言数组
  • 三,空指针并非空字符串
  • 四,整数溢出
  • 五,为main函数提供返回值
  • 六,声明和定义(extern)
  • 七,命名冲突与 static修饰符
  • 八,形参、实参和返回值
  • 九, 检查外部变量类型
  • 十,预处理器-不能忽视宏定义中的空格
  • 十一,预处理器-宏不是函数
  • 十二,宏不是类型定义
  • 十三,比较时把常量写在左边

一,函数指针和指针函数

float *g() , (*h)();

()的优先级大于 * , 因此 *g() 为 *( g() ) ,该函数 g() 的返回类型为指向浮点数的指针 float * 。

而h是一个函数指针,返回值类型为 float。

二,C语言数组

  1. C语言中只有一维数组,数组的大小需在编译时期确定(常数)数组的元素可以是任意类型的对象(包括数组)
  2. 对于一个数组,只能做两件事:1,确定数组的大小。2,获得下标为0的元素的指针。

以数组下标进行运算,实际上都是通过指针进行的。

三,空指针并非空字符串

#define NULL 0

当常数0被转换为指针使用时,这个指针绝对不能被解除引用(dereference),换言之,当我们将0赋值给一个指针变量时,绝对不能使用该指针所指向的内存中的存储内容。

四,整数溢出

无符号数无溢出

if((unsigned)a) + (unsigned)b > INT_MAX)

五,为main函数提供返回值

main函数与其他任何函数一样,若未显式声明返回类型,则默认为整型。

六,声明和定义(extern)

int a;

如果出现在所有函数体之外,则称为外部对象a的定义。a是一个外部整型变量,同时为a分配了存储空间,默认为0;

int a = 7;

在定义a的同时也为a明确指定了初始值。

extern int a; //这是一种引用,而非定义

a是一个外部整型变量,extern说明a的存储空间是在程序的其他地方分配的,只是一个外部对象的显式引用。

如果一个程序包含了 extern int a ,那么这个程序就必须在别的某个地方包括: int a; 那么这两个语句既可以在同一个源文件中,也可以位于程序的不同源文件之中。

七,命名冲突与 static修饰符

两个具有相同名称的外部对象,实际上代表同一个对象,如在两个不同的源文件都包括了定义:int a;
则或表示程序错误(禁止外部变量重复定义),或在两个源文件中共享a的同一个实例。

static 修饰符能解决命名冲突问题:

static int a;

static 具有文件作用域,将a的作用域限制在当前源文件中,对其他源文件,a并不可见。
static同样可以修饰函数,建议:若一个函数仅被同一个源文件中的其他函数调用,我们应当声明该函数为static。

八,形参、实参和返回值

任何C函数都有一个形参列表,列表中的每个参数都是一个变量,改变量在函数的调用过程中被初始化。

任何C函数都有自己的返回值:void/其他

如果一个函数在定义域之前被调用,那么它的返回值默认为 整型

  1. 如果一个函数在被定义或声明之前被调用,那么他的返回值类型默认为整型
  2. 在多文件中,若函数的定义和调用位于不同的文件中,则需要在调用前声明。

九, 检查外部变量类型

外部变量的定义和声明必须保持一致。
比如有两个文件,其中f2.c中定义了一个 long类型的变量n,但是在f1.c文件中却声明为int类型:

  • f1.c: extern int n;
  • f2.c:long n;

十,预处理器-不能忽视宏定义中的空格

如下所示为ref.h文件中定义的宏:

#ifndef _REF_H
#define _REF_H#define F(x) (x+1)
#define G (x) (x+1) #endif

在main函数中使用:

    int a = F(2);int b = G(3);

在使用 G(x)宏定义时出现如下错误:

错误原因在于G和 (x)之间多了一个空格,导致编译器将G作为一个宏,而不是G(x).

十一,预处理器-宏不是函数

注意:

  • 要把每个参数都用括号括起来
  • 整个表达式也要括起来
#define ABS1(a) ((a) > 0 ? (a) : -(a))
#define ABS2(a) (a > 0 ? a : -a)
#define MAX(a,b) ((a) > (b) ? (a) : (b))

如上代码片段中的两个宏定义所示,在宏定义ABS2(a)中,每个参数不用括号保护,当a=x-y的情况下,直接使用字符替换就会发生错误:

    int x = 4;int y = 6;int z1 = ABS1(x-y);int z2 = ABS2(x-y);

根据字符串简单替换的规则:

z1 = ABS1(x-y) = ((x-y) > 0 ? (x-y) : -(x-y)) = -2 > 0 ? -2 : 2 = 2
z2 = ABS2(x-y) = (x-y > 0 ? x-y : -x-y ) = -2 > 0 ? -2 : -4-6 = -10

所以使用括号来保护表达式,是很有必要的。

此外,还要确保宏中的参数不会出现二次变化(没有副作用),如以下代码片段:

int i = 0, arr = [2,4,6,8];
int a = MAX(arr[i++],1);

按照一般思维:i是先使用后自加,所以arr[i++]为2,MAX(2,1)结果等于2,即a等于2。
但是按照宏的简单字符串替换原则:

int a = MAX(arr[i++],1) = arr[i++] > 1 ? arr[i++] : 1 ;

当执行完 arr[i++] > 1 操作后, 还会执行 ? 后面的 arr[i++],此时i等于1,导致a的结果等于4;

最后还需注意的是 宏的嵌套,可能会产生非常庞大的表达式,始终要记住宏是简单的字符替换,而不是函数,C语言中函数可以使用递归(利用了栈的原理)来达到精简代码的目的,如以下代码:

MAX(a,MAX(b,MAX(c,d)));

将其展开后,程序将非常臃肿,不利于后期调试。

总而言之,宏是一种 以空间换时间 的一中策略,在这一点上,类似内联函数。

十二,宏不是类型定义

类型定义可以使用 typedef:

// 宏定义和typedef 一致
#define FOOTYPE struct foo
typedef struct foo FOOTYPE// T1和T2所表示的类型有可能会不一致
#define T2 struct foo *
typedef strcut foo * T1//比如
T1 a , b; // struct foo * a,b; a是指针类型,b是结构体类型
T2 a , b; // strcut foo * a, strcut foo *b; a和b都是指针类型

十三,比较时把常量写在左边

比如:

if( 'c' == a)
{// 推荐这种写法,当少写一个等于号=时,('c' = a)编译器会立即报错
}if( a == 'c')
{// 当少写一个等于号=时,(a = 'c')编译器不会报错,增加了调试难度
}

如上述程序所示,一个鲁棒性高,稳定性高的程序,一定要注意一个原则:

编译时报错 > 运行时报错

也就是说,程序中的bug要早发现早解决,程序运行过程中出现的bug其查找难度倍增。

《C陷阱与缺陷》读书笔记与总结相关推荐

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

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

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

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

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

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

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

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

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

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

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

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

  7. C陷阱与缺陷(C Traps and Pitfalls)读书笔记

    说在前面:为了监督自己养成主动读书获取知识的习惯,特此写一篇博客作为读书笔记,由于是第一次主动正式写读书笔记,免不了语言错误或有不妥的地方,欢迎直接指出,不需要给博主留面子!

  8. 【读书笔记】《能力陷阱》——改变思想从行动开始

    内容简介 <能力陷阱>原译书名为<逆向管理>英文书名为<Act like A leader Think like A leader>作者尼亚 伊贝拉,现书名是樊登老 ...

  9. 读书笔记软件调试之道 :问题的核心-如何修复缺陷

    声明:本文档的内容主要来源于书籍<软件调试修炼之道>作者Paul Butcher,属于读书笔记.欢迎转载! ---------------------------------------- ...

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

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

最新文章

  1. opencv-contrib配置过程
  2. 谈C++求a+b(大神勿喷)
  3. How to make a Logical Volume ON AIX5.3
  4. Word -- 列表重新编号
  5. Java面试进阶:Dubbo、Zookeeper面试题锦集
  6. 安装pycrypto 2.6.1各种坑
  7. 前端每日实战:151# 视频演示如何用纯 CSS 创作超能陆战队的大白
  8. java 修改Chrome浏览器的默认下载路径
  9. win7 右键无法新建txt
  10. native react 图片多选_N-第三方-react-native-image-picker,选择图片上传
  11. MATLAB实现变限积分函数的积分/ 多重积分/ 如何解决求积分显示AB浮点标量报错
  12. H5首屏图片加载优化
  13. AnyDesk 未连接到服务器。请检查您的互联网连接
  14. mysql怎样添加唯一标识_MySql设置唯一标识,主键等设置
  15. Flowerpot(单调队列)
  16. Openwrt-Chaos_Calmer搭建编译环境问题处理
  17. 【Proteus仿真】| 51单片机——MAX7219 驱动数码管
  18. acl 影响因子_计算机系部分会议级别和影响因子(DB、AI相关方向)
  19. 【软件测试】敏捷方法与测试左移
  20. symbian 模拟器 相关

热门文章

  1. 使用js获取input file的路径C:\fakepath\问题
  2. Ubuntu 15.10 安装 Rabbitvcs
  3. 咪咕音乐HTML代码,咪咕音乐播放器如何更换皮肤和底纹技术分享
  4. pe显示linux分区文件,找到了linux分区顺序错乱修复方法
  5. c语言代码怎么转成java,急C语言代码转成java代码,该如何解决
  6. Spring核心组件
  7. Pos58打印程序开发相关
  8. pcn变更流程图_超经典的工程变更流程图
  9. UG工程图自动标注工具 64位 版本无限制
  10. Java基础知识——JavaIO