Note

本篇是《C Primer Plus》附录 B(Reference Section)的中文速查版。原书按主题分成九节:附加阅读、C 运算符、基本类型与存储类别、表达式与语句与程序流、标准 ANSI C 库(含 C99 增补)、扩展整数类型、扩展字符支持、C99 数值计算增强、C 与 C++ 的差异。这里把它重排为便于随手翻查的表格——写代码卡壳时,先来这一篇找答案,再去原书对应章节看细节。

18.1 运算符优先级全表

按优先级从高到低排列(同格内按结合性从左到右或从右到左结合)。除注明外均为二元运算符;注意 *(乘法)与 *(解引用)、-(减法)与 -(负号)同符号但优先级不同。

优先级运算符结合性说明
1(最高)++ --(后缀)、()(函数调用)、[]{}(复合字面量)、.->从左到右后缀自增/自减、下标、成员访问
2++ --(前缀)、- +(一元)、~ !sizeof*(解引用)、&(取地址)、(type)(强制转换),均为一元从右到左单目运算符一族
3(类型名)从右到左强制类型转换
4* / %从左到右乘除取模(% 仅限整数)
5+ -(二元)从左到右加减
6<< >>从左到右左移、右移
7< > <= >=从左到右关系比较
8== !=从左到右相等比较(注意别写成 =
9&从左到右按位与
10^从左到右按位异或
11|从左到右按位或
12&&从左到右逻辑与(短路求值)
13||从左到右逻辑或(短路求值)
14? :(条件表达式)从右到左唯一的三目运算符
15= *= /= %= += -= <<= >>= &= |= ^=从右到左赋值与复合赋值
16(最低),(逗号运算符)从左到右先求左表达式,整体取右表达式的值

记忆抓手:后缀 > 一元 > 乘除 > 加减 > 移位 > 关系 > 相等 > 位运算(与、异或、或)> 逻辑与 > 逻辑或 > 条件 > 赋值 > 逗号

/图:for 语句三段控制表达式与循环执行流程

18.2 关键字速查

C 的基本数据类型由 8 个关键字组合而成:intlongshortunsignedcharfloatdoublesigned(ANSI C);C99 又新增 _Bool_Complex_Imaginarylong longrestrict 等。

类别类型/关键字要点
有符号整数int系统的基本整数类型,宽度依实现而定
long / long int至少 32 位,不小于 int
short / short int至少 16 位,不大于 int
long long(C99)至少 64 位,不小于 long
无符号整数unsigned int/long/short/long long只表示 0 和正数,扩大正数范围;单独的 unsigned 等价 unsigned int
字符char按定义占 1 字节;signed charunsigned char 是两个独立类型
布尔_Bool(C99)只取 0/1;含 stdbool.h 后可用 booltruefalse
实浮点float至少 6 位有效数字,通常 32 位
double至少 10 位有效数字,通常 64 位
long double可能比 double 更大更精
复数/虚数(C99)float/_Complex内部按两个实数元素的数组存储(实部、虚部);虚数类型可选

存储类别(storage class)决定变量的作用域、链接与存储期:

存储类别存储期作用域链接声明方式
自动(auto)自动块内(默认)
寄存器(register)自动块内加 register
静态外部链接静态文件外部所有函数之外(默认)
静态内部链接静态文件内部所有函数之外加 static
静态无链接静态块内加 static

类型限定符(qualifier):

限定符含义
const初始化后不可修改。const int * p:p 指向的数据不可改;int * const p:p 本身不可改
volatile提醒编译器该变量可能被程序之外的因素(如硬件)改变,禁止过度优化
restrict(C99)承诺该指针是相应内存块的唯一访问途径(如函数原型 void interleave(int * restrict p1, int * restrict p2, int n) 隐含两块内存不重叠)

18.3 语句与程序流速查

语句类别形式要点
while入口条件循环while (表达式) 语句先判断后执行,循环体可能一次都不跑
for入口条件循环for (初始化; 测试; 更新) 语句三个控制表达式用分号隔开;C99 允许初始化部分声明变量(作用域限于循环)
do while出口条件循环do 语句 while (表达式);先执行后判断,循环体至少执行一次
if / if else分支if (表达式) 语句 else 语句表达式非零即为”真”,可级联 else if
switch多重分支switch (表达式) { case 常量: ... default: ... }表达式与标签必须是整数值;匹配后一直执行到 break 或结尾(穿透)
break跳转break;终止所在循环或 switch;不能用于循环外的 if
continue跳转continue;跳过本轮剩余语句;while/for 直接进入测试,do while 先测退出条件
goto跳转goto 标签; / 标签: 语句可前跳可后跳;现代代码尽量少用
复合语句{ 语句... }花括号包裹的块,等效于一条语句

18.4 标准库头文件总览

ANSI C 库按功能分组,每组对应一个头文件。下表为”按需取用”的导航图:

头文件主题代表内容
assert.h诊断assert() 宏:表达式为假时打印诊断信息并调用 abort();定义 NDEBUG 可关闭
ctype.h字符处理isalpha()isdigit()isspace()toupper()tolower()
errno.h错误报告errno 及宏 EDOM(定义域错误)、ERANGE(值域错误)、EILSEQ(宽字符转换错误)
float.h浮点特性FLT_RADIXFLT_MAXDBL_DIG 等实现常量
limits.h整型限制INT_MAXCHAR_BITLONG_MIN
locale.h本地化setlocale()localeconv()LC_ALL 等类别宏
math.h数学库sin()sqrt()pow()ceil()fabs() 及 C99 新函数(见 18.6)
setjmp.h非本地跳转setjmp() 保存执行环境,longjmp() 跳回;用于错误处理
signal.h信号处理signal() 设置信号响应,raise() 发送信号;宏 SIGINTSIGABRT
stdarg.h可变参数va_list 类型与 va_start()va_arg()va_end()va_copy()
stdbool.h(C99)布尔支持bool_Booltrue→1、false→0
stddef.h常用定义size_tptrdiff_twchar_t 类型与 NULLoffsetof()
stdint.h(C99)扩展整数int32_tuint_least16_t 等定宽类型(见 18.7)
inttypes.h(C99)扩展整数 I/O包含 stdint.h,提供 PRId32SCNd32 等格式宏
stdio.h标准 I/Oprintf()scanf()fopen()fgets() 等全套输入输出
stdlib.h通用工具内存管理、随机数、字符串转换、排序查找、exit()
string.h字符串处理strlen()strcpy()strcmp()strcat()strchr() 及内存函数
time.h日期时间clock()time()strftime()struct tm
complex.h(C99)复数complex 宏、虚数单位 Icsin()creal()cimag()
fenv.h(C99)浮点环境状态标志与控制模式:fesetround()feclearexcept()
iso646.h(C99)运算符别名and&&or||not!
tgmath.h(C99)通用数学宏按实参类型自动展开成 sin/sinf/sinl/csin 等对应函数
wchar.h(C99)宽字符工具wprintf()wcscpy() 等宽字符版 I/O 与字符串函数
wctype.h(C99)宽字符分类iswdigit()towlower() 等宽字符版字符函数

18.5 常用库函数速查(按头文件分组)

ctype.h —— 字符判断与转换

函数返回真的条件(“真”即非零)
isalnum(c)字母或数字
isalpha(c)字母
isblank(c)空格或水平制表符(C99)
iscntrl(c)控制字符(如 Ctrl+B)
isdigit(c)数字
isgraph(c)除空格外的可打印字符
islower(c) / isupper(c)小写 / 大写字母
isprint(c)可打印字符
ispunct(c)标点(可打印但非字母数字空格)
isspace(c)空白字符:空格、换行、换页、回车、垂直/水平制表符
isxdigit(c)十六进制数字
tolower(c) / toupper(c)返回小写 / 大写形式;其余情况原样返回

stdio.h —— 标准 I/O 高频函数

函数功能
printf() / scanf()标准输出 / 输入的格式化 I/O
fprintf() / fscanf()对指定流的格式化 I/O
sprintf() / sscanf() / snprintf()以字符串为对象的格式化 I/O(snprintf 限长,更安全)
getchar() / putchar()标准输入 / 输出单个字符
gets() / puts()整行读取(无长度限制,危险)/ 输出字符串
fgets() / fputs()对指定流的整行读写
fopen() / fclose()打开 / 关闭文件,返回 FILE *
fgetc() / fputc() / getc() / putc()流上的单字符读写
fread() / fwrite()二进制块读写
fseek() / ftell() / rewind()移动 / 查询 / 归零文件位置指针
feof() / ferror() / clearerr()测试文件尾 / 错误 / 清除指示器
remove() / rename() / tmpfile()删除 / 重命名文件、创建临时文件
ungetc()把字符推回流上

stdlib.h —— 内存、随机数与转换

函数功能
malloc(size)分配 size 字节(不初始化),成功返回地址,失败返回 NULL
calloc(nmem, size)为 nmem 个元素分配空间并全部清零
realloc(ptr, size)调整已分配块的大小,内容保留到新旧尺寸中较小者
free(ptr)释放 malloc/calloc/realloc 分配的空间
rand() / srand(seed)伪随机数(0 到 RAND_MAX)/ 设置随机种子
atoi() / atol() / atof()字符串转 int / long / double(失败返回 0)
strtol() / strtoul() / strtod()更健壮的字符串转换:可指定进制、报告转换终止位置
exit(status) / abort()正常退出(先执行 atexit 注册函数)/ 异常终止
atexit(func)注册程序正常结束时调用的函数(逆序执行)
qsort(base, nmem, size, comp)任意类型数组的快速排序,比较函数由用户提供
bsearch(key, base, nmem, size, comp)有序数组的二分查找
system(str) / getenv(name)执行宿主命令 / 读取环境变量

string.h —— 字符串与内存

函数功能
strlen(s)字符串长度(不含结尾空字符)
strcpy(s1, s2) / strncpy(s1, s2, n)复制字符串 / 至多 n 个字符
strcat(s1, s2) / strncat(s1, s2, n)追加字符串 / 至多 n 个字符
strcmp(s1, s2)比较字符串:负 / 0 / 正表示小于 / 等于 / 大于
strncmp(s1, s2, n)只比较前 n 个字符
strchr(s, c) / strrchr(s, c)查找字符首次 / 最后一次出现的位置
strstr(s1, s2)查找子串 s2 在 s1 中首次出现的位置
strpbrk(s1, s2)查找 s2 中任一字符在 s1 中的首次出现
strspn() / strcspn()计算”全在 / 全不在 s2 字符集”的前缀长度
strtok(s, seps)按分隔符把字符串切成 token;后续调用首参传 NULL
strerror(errnum)返回错误编号对应的消息字符串
memcpy() / memmove()复制 n 字节;后者允许内存重叠,更安全
memcmp() / memchr() / memset()比较 / 查找 / 填充 n 字节内存

math.h —— 数学函数(double 版)

函数功能
sin() cos() tan()asin() atan() atan2(y, x)三角与反三角(弧度制)
sinh() cosh() tanh()双曲函数
exp() log() log10()e^x、自然对数、常用对数
pow(x, y) sqrt() cbrt()(C99)乘方、平方根、立方根
fabs() ceil() floor() fmod(x, y)绝对值、向上 / 向下取整、x/y 余数
hypot(x, y)(C99)√(x²+y²)
round() trunc() rint()(C99)四舍五入(半值远离零)/ 截断 / 按当前舍入方向取整
fmax() fmin() fdim()(C99)最大值 / 最小值 / 正差
isnan() isfinite() isnormal()(C99 宏)数值分类:NaN、有限、正常

18.6 扩展整数类型(stdint.h / inttypes.h)

C99 用 typedef 提供按位宽命名的整数类型,配套 PRI…/SCN… 格式宏用于 printf()/scanf()

#include <inttypes.h>
int32_t cd_sales;                          /* 无论平台,恒为 32 位 */
scanf("%" SCNd32, &cd_sales);
printf("CD sales = %10" PRId32 " units\n", cd_sales);
类型家族形式特点
精确宽度intN_t / uintN_t(N=8,16,32,64)恰好 N 位;个别系统可能不支持某宽度
最小宽度int_leastN_t / uint_leastN_t至少 N 位;一定存在
最快最小宽度int_fastN_t / uint_fastN_t至少 N 位且运算最快的一档
最大宽度intmax_t / uintmax_t系统中最宽的有符号 / 无符号整数
可存指针intptr_t / uintptr_t能无损容纳指针值的整数(可选)

/图:扩展整数类型的 printf/scanf 格式宏与取值范围常量

18.7 C 与 C++ 的主要差异一览

把 C 程序丢给 C++ 编译器时,以下几处最容易踩坑:

差异点CC++
函数原型可选;空括号表示”未声明参数”强制;空括号等于 (void)
char 常量int 处理('ABCD' 可当 4 字节整数)char 处理
全局 const外部链接内部链接(相当于 C 的 static const
结构标签作类型名须写 struct duo m;可直接写 duo n;
枚举赋值整数赋给枚举变量仅告警必须显式强转,season++ 也非法
void * 赋给其他指针合法必须显式强转
布尔类型_Bool(含 stdbool.h 后用 bool内建 booltrue/false 是关键字
运算符别名#include <iso646.h>andor 等是关键字

速查使用姿势

  • 卡在优先级:回到 18.1 从下往上数层级,拿不准就加括号——括号永远免费。
  • 忘记函数原型:先查 18.4 定位头文件,再到 18.5 对应分组找函数签名。
  • 需要跨平台固定位宽:用 18.6 的 int32_t 一族 + 格式宏,别再赌 int 的宽度。
  • const 与指针的先后顺序:读 18.2 限定符表那两行——const* 左边锁数据,在 * 右边锁指针。
  • 本篇只收录高频内容;完整原型与罕见函数请查阅原书附录 B 或实现文档。