前言
这是我在大一时 C 语言课程考试前总结的坑们,每次相关考试前都会复习一次。如果你发现可以补充的地方,欢迎评论。
正文
一言以概任何看上去有简单答案的问题都藏有坑。
看见定义变量,总是注意变量是否被初始化过。
注意 if 中的等号是 = 还是 ==。
if 语句没有花括号时只包含之后一行,如:
1 | if (condition) |
if else语句在没有花括号时,else总是匹配最近的if(Dangling Else),如:
1 | if (a) |
实际上是:
1 | if (a) { |
switch语句在缺少 break 时将 Fall through。
求值逻辑表达式时 真为 1, 假为 0。 判断逻辑表达式时, 非 0为 真,0为 假。
true、false、TRUE、FALSE 是合法的变量名,因为在 C 中它们不是关键字。
*p++等价于 *(p++),因后自增++ 优先级高于解引用*。
注意整型除法结果被截断, int n = 2; 1 / n == 0。
注意本地变量需要初始化。
注意变量作用域屏蔽,如:
1 | int b; |
注意函数为传值,指针也是值传递,例如:
1 | void swap(int *a, int *b) { |
并不影响外部。
注意 int 溢出,若易发生溢出(如阶乘操作)应使用 long 或double代替。
char a = 255,打印后值为 -1,因为 char 为有符号类型并采用补码表示,其范围为 -128 ~ 127。
负数的位移操作中符号位也参与位移;左操作数为负数的右移 >> 结果由实现定义,Turbo C 为补 1。
a/b与 a%b 结果在 a 与b中有一个为负数时由实现定义,但保证 a / b * b + a % b 结果为a。
过滤回车结束的输入字符串:
1 | i = 0; |
C 语言二维数组存储采用 Row-major 方式,即在内存中存储为 row1, row2, row3…,初始化时必须给出列数以确定行中元素个数,静态变量中未初始化元素自动为 0,使用时可以将列数溢出到下一行,即偏移值计算为 row_count * row + column。
注意分号:
1 | for (...); |
do_something只执行一次。
1 | if (0); |
do_something一定会被执行。
sizeof()表达式的值在编译时确定。编译器不计算其中表达式的值,仅将其替换为对应类型。sizeof应用于数组时结果为 数组元素个数 * 元素大小,应用于指针时为指针变量长度(在 32 位机器上地址长度为 32,故值为 8;64 位机器上为 16)。
无论 x 是数组还是指针,在定义上编译器认为 x[3] 与*(x+3)是等价的。但根据 x 的类型是数组还是指针, 编译器将为 x+3 或sizeof(x)生成不同的代码。
类似 char [] 类型的数组名被视为指向 char 的指针,char[][]也被视为指向 char[] 的指针。
注意 && 或||具有 短路求值 特性,不会执行无必要求值的表达式。
注意含中文字符的文件应保存为 GB* 编码,例如GB18030。
strcpy(dst, src)中目标在前,源在后。
注意 int a, b, c,c = 2a + b 非法(需要*)。
注意 x<=y<=z 意为(x<=y)<=z,意义非预期但合法。
scanf遇到空白字符截止,为输入一行可使用 scanf("[^\n]",str) 或gets()。
注意:scanf("%s", s)输入 "How are you?" 遇到空格截断,只得到How。
strcpy(char *dst, char *src) { while(*dst++ = *src++); };中 ++ 优先级高于*
[]、()优先级高于 *,故int *array[1] 等价于 (int *)array[1],为 int 指针的数组;int (*p)[1] 为指向 int 数组的指针。
解读方法:以 int **a[1][2] 为例。从 a 出发,优先向右解读。(来源)
ais …intaisarrayof 1 …intaisarrayof 1arrayof 2 …intaisarrayof 1arrayof 2pointers to …intaisarrayof 1arrayof 2pointers topoinertoint
优先级:后置自增 / 自减 、函数调用、数组元素、结构成员 > 前置自增 / 自减、正负、类型装换、 解引用 、取地址、数据类型大小、内存操作、(逻辑、位)非 > 结构成员解引用 > 乘除、取余 > 加减 > 位移 > 比较 > 等价 > (逻辑、位)与、或、异或 > 三元条件、(复合)赋值。
字符串字面值与字符常量中转义序列 \ddd 可以为 1、2、3 位 8 进制数(07),上限为 377;F)。\xhh可以为 1、2 位十六进制数(0
转义序列列表:
| 转移序列 | 含义 |
|---|---|
\a |
BEL |
\b |
BS |
\f |
FF |
\n |
LF |
\r |
CR |
\t |
HT |
\v |
VT |
\\ |
\ |
\' |
' |
\" |
" |
\0 |
NULL |
\ddd |
八进制 |
\xhh |
十六进制 |
0~255 的数字也可以作为有效的字符取值。
int i = -1; printf("%d", (unsigned int)i);打印出 -1,因为%d 为有符号整型。
使用草稿纸记录变量取值以计算程序输出,或对某些类型可理解程序意图猜测程序输出,或两者结合。
10^-6表示为1e-6。
通过 memset(array,0,sizeof(array)) 可以实现数组重初始化。(需要string.h)
宏 # 代表将此后的文本变为字符串;##代表连接文本;含有 # 与##的宏不进行参数展开,可利用包装宏展开。(参考)
宏函数定义:
1 |
调用与函数调用一致:
1 | func_name(var); |
scanf中 f 为float,lf为 double;printf 中f为 double;lf 未定义。
,逗号表达式对左侧表达式求值并丢弃返回值,之后对右侧表达式求值并返回其返回值。
#define SWAP(a,b) (a)^=(b)^=(a)^=(b)可实现无临时变量的交换。
| 字符 | ASCII |
|---|---|
| 0 | 48 |
| A | 65 |
| a | 97 |
变量命名法则:[A-Za-z_][A-Za-z0-9_]*,且不含关键字。
auto,register,volatile均为 C 关键字。
007为八进制数字,注意范围为 0-7,0x0F 为十六进制数字。
strcat实现:
1 | void strcat(char *str1, char *str2) { |
相关阅读《C 陷阱与缺陷》笔记 | 孙耀珠的博客