我的《C陷阱与缺陷》读书笔记
第一章 词法“陷阱”
1、 =不同于==
if(x = y)
break;
实际上是将y赋给x,再检查x是否为0。
如果真的是这样预期,那么应该改为:
if((x = y) != 0)
break;
2、 &和| 不同于 && 和 ||
3、词法分析中的“贪心法”
编译器将程序分解成符号的方法是:从左到有一个一个字符的读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符床是否可能是一个符号的组成部分;如果可能,继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号。例如:
y = x/*p; 会被解析为:/* 注释符号
4、整型常量
010(八进制数) 不同于 10(十进制)。
5、字符与字符串
首先是单引号与双引号的区别:
用单引号括起来的一个字符表示一个整数(ASCII码),而双引号括起来表示一个指针。
第二章 语法“陷阱”
1、理解函数声明
弄懂(*(void(*)())0)(); //首地址为0的函数。
float (*h)(): h是一个指向返回值为浮点型的函数的指针
所以,(float (*)()) 表示一个“指向返回值为浮点型的函数的指针”的类型转换符。
fp(): 是(*fp)( )的简写。
*fp():是 *( (*fp) ( ) )的简写。
( *0 )( );
虽然上式编译器不认,但可以把0转换为指向“返回值为void的”函数的指针,所以0可变为: ( void(*) ( ) ) 0 ,代入(*0)(),得到:
(*( void(*) ( ) ) 0) ( ) 。
该式子用等价于:
typedef void ( *func ) ( );
( *( func ) 0 ) ( );
类似的,signal.h中对signal函数的声明:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
2、运算符优先级的问题
需要补充表格
3、其他
主要是别多写分号,switch别忘了break,别写空else分支。
第三章 语义“陷阱”
1、指针与数组
Struct {
Int p[4];
Double x;
}b[17];
Int calendar[12][31];
Int (*p)[31];
Sizeof(calendar):12*31=372
calendar[0]指向该一维数组 对应*p
calendar[0][0]
......
calendar[0][30]
calendar[1]指向该一维数组 对应*(p+1)
calendar[1][0]
......
calendar[1][30]
…….
......
……
calendar[11]指向该一维数组对应*(p+11)
calendar[11][0]
......
calendar[11][30]
2、内存分配
free(r);
用malloc显式分配的空间,不会再退出本函数后自动释放掉,而是会等程序员显式释放后才消失。
注意检查,malloc分配的内存可能失败。
C语言中会自动地将作为函数参数的数组声明转换为对应的指针声明,如:
int strlen(char s[ ]){ }等价于int strlen(char *s){ }
但在其他情形下不会自动转换,也就是说不等价,如:
extern char hello[ ];和extern char *hello;完全不同。
边界计算
自己实现一个memcpy函数:
void memcpy(char *dest, const char *source, int k)
{
while( --k >= 0 )
*dest++ = *source++;
}
说到底就是,操作时一定要知道操作数据的长度。
整数溢出
两个有符号整数相加会发生溢出。
两个无符号整数相加不会发生溢出。
一个有符号和一个无符号整数相加,因为有符号被自动转换成无符号,所以也不会溢出。
第四章 连接
编译器一般每次只处理一个文件。编译器的责任是把C源程序翻译成对连接器有意义的形式。
许多系统中的连接器是独立于C语言实现的,因此如果链接时候错误原因是与C语言相关的,连接器无法判断错误原因。但连接器能够理解机器语言和内存布局。
典型的连接器把由汇编器或编译器生成的若干个目标模块,整合成一个被称为载入模块或可执行文件的实体。
连接器通常把目标模块看成是由一组外部对象组成的。每个外部对象代表着机器内存中的某个部分,并通过一个外部名称来识别。因此,程序中的每个函数和每个外部变量,如果没有被声明为static,就都是一个外部对象。static的不会与其它源程序文件中的同名函数或同名变量发生冲突。对于非satatic的函数或变量的名称冲突的解决办法将在后面讨论。
除了外部对象外,目标模块中还可能包括了对其他模块中的外部对象的引用,当连接器读入一个目标模块时,它必须解析出这些引用,并作出标记说明这些外部对象不再是未定义的。
连接器的输入是一组目标模块文件和库文件。输出是一个载入模块。
避免外部变量的函数的冲突和不一致等问题的办法:
每个外部对象只在一个头文件里声明,需要用到该外部对象的所有模块都应该包括这个头文件。定义该外部对象的模块也应该包括这个头文件。
第五章 库函数
没什么好说的,就是apue的一些函数而已。
第六章 预处理器
宏定义:主要是理解宏不是函数,而是直接替换
1、 不能忽视宏定义中的空格:
#define f (x) ( (x)-1 ):因为f后面多了一个空格,所以f(x)代表(x) ( (x)-1 )
2、 宏并不是函数,所以注意那些括号:
如:#define abs(x) ( ( (x) >= 0)?(x):-(x) )
#define max(a,b) ( (a)>(b)?(a):(b) )
3、 宏并不是语句
#define assert(e) if (!e) assert_error(__FILE__, __LINE__)
4、 宏不是类型定义
错误用法: #define int_8_ int*
int_8 a,b; //则a是指针,b是int型
正确用法应该用typedef
typedef int * int_8_;
第七章 可移植性缺陷
主要是:
1、 应对C语言标准的变更;
2、 标识符名称的限制;
3、 整数的大小;
4、 字符是有符号整数还是无符号整数;
5、 移位运算符;
1)在向右移位时,空出的位是由0填充还是1,还是由符号位的副本填充?
如果被移位对象是无符号数,那么由0填充;如果是有符号数,那么是0或符号位的副本。
2)移位操作的位数允许的取值范围是什么?
如果被移位对象的长度是n位,那么移位计数必须大于或等于0,而严格小于n。
6、 移植性需考虑的地方:
1)机器的字符表不同。
2)有的机器是one's complement,有的机器是two's complement的。基于2的补码的计算机,所允许表示的附属取值范围要大于正数取值范围,所以有时取负值的运算会导致溢出。
3)各机器对取模运算的定义不同。
第八章 惯用与答案
将惯用的c == '\t' 写作 '\t' == c。一旦写错成=号,编译器就能检查出来。
我的《C陷阱与缺陷》读书笔记的更多相关文章
- C陷阱与缺陷读书笔记
2.1理解函数声明 这一章仔细分析了(*(void(*)())0)();这条语句的含义,并且提到了typedef的一种函数指针类型定义的用法. 我们经常用到的typedef用法是用于指定结构体的类型, ...
- C陷阱和缺陷学习笔记
这段时间把<C陷阱和缺陷>看了,没时间自己写总结.就转一下别人的学习笔记吧http://bbs.chinaunix.net/thread-749888-1-1.html Chapter 1 ...
- C的陷阱和缺陷研读笔记01
词法分析: 编译器将程序分解成符号的方法是 从左到右一个一个字符的读入,如果该字符可能组成一个符号,再读入下一个字符 而c语言里的符号 / * =只有一个字符长, 是单字符的, /* == 一些事双字 ...
- 软件测试价值提升之路- 第三章"拦截缺陷 "读书笔记
作为一个测试团队,基本的职责是:测试产品,发现缺陷,报告结果,使每个版本的测试水准稳步提升.这些价值是作为一个测试所必须具备的,发挥这些价值能够让测试获得研发团队的基本信任.这类价值分为3部分: 1) ...
- C陷阱与缺陷学习笔记
导读 程序是由符号(token)序列所组成的,将程序分解成符号的过程,成为"词法分析". 符号构成更大的单元--语句和声明,语法细节最终决定了语义. 词法陷阱 符号(token)指 ...
- C的陷阱和缺陷研读笔记02
宏: 宏不是函数 展开会产生庞大的表达式 #define MIN(A,B) ((A) <= (B) ? (A) : (B))MIN(*p++, b)会产生宏的副作用 剖析: 这个面试题主要考查面 ...
- 《c陷阱与缺陷》笔记--注意边界值
如果要自己实现一个获取绝对值的函数,应该都没有问题,我这边也自己写了一个: void myabs(int i){ if(i>=0){ printf("%d\n",i); }e ...
- 《c陷阱与缺陷》笔记--移位运算
#include <stdio.h> int main(void){ int a = 2; a >> 32; a >> -1; a << 32; a & ...
- 读书笔记--C陷阱与缺陷(七)
第七章 1.null指针并不指向任何对象,所以只用于赋值和比较运算,其他使用目的都是非法的. 误用null指针的后果是未定义的,根据编译器各异. 有的编译器对内存位置0只读,有的可读写. 书中给出了一 ...
- 读书笔记--C陷阱与缺陷(一)
要参与C语言项目,于是作者只好重拾C语言(之前都是C++,还是C++方便). 看到大家都推荐看看 C陷阱与缺陷(C traps and pitfalls),于是好奇的开始了这本书的读书之旅. 决定将 ...
随机推荐
- 加解密 3DES AES RSA 简介 示例 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- 数据库插入数据返回当前自增主键ID值的方法
当我们插入一条数据的时候,我们很多时候都想立刻获取当前插入的主键值返回以做它用.我们通常的做法有如下几种: 1. 先 select max(id) +1 ,然后将+1后的值作为主键插入数据库: 2. ...
- [React] Safely setState on a Mounted React Component through the useEffect Hook
In the class version of this component, we had a method called safeSetState which would check whethe ...
- 淘宝网前端开发面试题(二)--JS 面试题
所有答案仅供参考,不负责答案对错(^_^) 1.js 是什么,js 和 html 的开发如何结合? js是javascript的缩写,是一种基于对象的.事件驱动的脚本语言.它一共由三个部分组成:分别是 ...
- Verilog 基础回顾 (一)
Verilog 大小写敏感,且所有关键字都是小写 1 寄存器 register = storage,是数据存储单元的抽象,可视为能够存储数值的变量 (variable that can hold v ...
- 内存映射(Linux设备驱动程序)
第一部分:mmap系统调用直接将设备内存映射到用户进程的地址空间里. 第二部分:跨越边界直接訪问用户空间的内存页.一些相关的驱动程序须要这样的能力,(用户空间内存怎样映射到内核中的方法get_user ...
- 移动端HTML5框架
一:移动端HTML5框架 http://jquerymobile.com/jQuery Mobile http://jqtjs.com/jQTouch http://www.sencha.com/pr ...
- Java 并发:内置锁 Synchronized
摘要: 在多线程编程中,线程安全问题是一个最为关键的问题,其核心概念就在于正确性,即当多个线程訪问某一共享.可变数据时,始终都不会导致数据破坏以及其它不该出现的结果. 而全部的并发模式在解决问题时,採 ...
- Java之字节码(1) - 深入解析
转载地址 一:Java字节代码的组织形式 类文件{ OxCAFEBABE,小版本号,大版本号,常量池大小,常量池数组,访问控制标记,当前类信息,父类信息,实现的接口个数,实现的接口信息数组,域个数,域 ...
- 小米电视2S加量不加价,你还会买吗?
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/ ...