C Traps and Pitfalls 读书笔记
第1章 词法陷阱
int getc(FILE *stream)
从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。
本意: 跳过文件中的SPACE, TAB, ENTER
while (c = ' ' || c == '\t' || c == '\n')
c = getc(f);
实际将 ' ' || c == '\t' || c == '\n'
赋值给 c
, ' '
ASCII值为32,!=0, 所以该表达式为 1
即 while (c = 1)
-
如果int常量第一个字符是0,作为八进制处理。即使是0195也会被视作
1*8^2 + 9*8 + 5
, 即0215 -
对于使用ASCII字符的集编译器,
''
引起字符,与整数严格对应。""
引起字符串,由’\0’结尾,是指向数组起始字符的pointer。
char *slash = '/'; // ERROR '/'不是字符指针
printf('\n'); // ERROR
printf("\n"); // RIGHT
第2章 语法陷阱
- 函数声明:
<type> <declarator>
float ff(); // return value 为 float 的 func
float *g(); // g()的返回值为指向float数的指针
float* g(); // 我曾经喜欢这么写,笑死
float (*h)(); //h是函数指针, 所指向函数的返回值是float
- 对于函数指针的函数调用,
fp
是函数指针,(*fp)()
即可调用该函数的方式,可简写为fp()
希望调用首地址为0的子例程:
(void (*)()) 0
将0
转化为返回值为void类型的函数指针
((void (*)()) 0)();
调用它
else
始终与同一()
内最近的未配对if
配对
if (x == 0) // A
if (y == 0) error; // B
// else 与 B 处 if 配对
else{
z = x + y;
}
这个其实敲得时候感觉很明显啊,A处的if
应该会有一个{}
把剩下的if
和else
括起来。
第3章 语义陷阱
- C 中只有一维数组
b是有17个元素的数组,每个元素是个结构体
struct {
int p[4];
double x;
}b[17];
int c[12][31]
c是一个数组,有12个元素,即c[12]
,每个元素是有31个int元素的数组(可以看做int arr[31]
, arr代表c[12]
)
第4章 连接
C 是separate compilation, 单独编译,由连接器整合。
static (1)在修饰变量的时候,static 修饰的静态局部变量只执行初始化一次,而且延长了局部变量的生命周期,直到程序运行结束以后才释放。 (2)static 修饰全局变量的时候,这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是 extern 外部声明也不可以。 (3)static 修饰一个函数,则这个函数的只能在本文件中调用,不能被其他文件调用。static 修饰的变量存放在全局数据区的静态变量区,包括全局静态变量和局部静态变量,都在全局数据区分配内存。初始化的时候自动初始化为 0。 (4)不想被释放的时候,可以使用static修饰。比如修饰函数中存放在栈空间的数组。如果不想让这个数组在函数调用结束释放可以使用 static 修饰。 (5)考虑到数据安全性(当程序想要使用全局变量的时候应该先考虑使用 static)。
extern可以在一个文件中引用另一个文件中定义的变量或者函数
第5章 库函数
<errno.h>
头文件中有一个 errno 宏,它就用来存储错误代码,当系统调用或者函数调用发生错误时,就会将错误代码写入到 errno 中,再次读取 errno 就可以知道发生了什么错误。
使用 perror()
将错误信息(文本)打印到标准输出设备;
使用 strerror()
将错误代码转换成对应的文本信息。
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{
errno = 0; //将 errno 设置回 0 值
FILE *fp = fopen("./demo.txt", "r");
if(fp){
printf("Congratulations, the file opens successfully!\n");
}else{
printf("Error no.%d: %s\n", errno, strerror(errno));
}
return 0;
}
第6章 预处理器
宏
第7章 可移植性缺陷
略