》附录 A-E 全解析:从 ASCII 到库函数速查)
1. 引言谭浩强老师的《C程序设计》是国内影响最深远的 C 语言教材之一而书末的附录虽然常被当作“参考资料”略过实际上却是初学者从“能看懂代码”走向“会查手册”的关键跳板。本文把第五版附录 A~E 的核心内容整理成一篇速查笔记覆盖ASCII 字符表、关键字、运算符优先级、语法提要、常用库函数五大块既可作为期末复习提纲也可作为日常编码时的案头手册。需要说明的是附录 A~E 的内容本质上是 C 语言国际标准与基础规范本文以“知识点整理”的方式重述并补充了记忆技巧和常见陷阱。2. 附录 A常用字符与 ASCII 代码对照表ASCIIAmerican Standard Code for Information Interchange是字符编码的基石。C 语言中char类型本质上存储的就是一个 ASCII 码值因此字符和整数可以相互比较、运算这是理解附录 A 的前提。2.1 核心记忆点字符类别十进制范围代表字符记忆锚点控制字符0 ~ 32、127NUL、LF、ESC、DEL不可打印空格32 第一个可打印字符数字48 ~ 570~90等于 48大写字母65 ~ 90A~ZA等于 65小写字母97 ~ 122a~za等于 97只需记住三个锚点0 48、A 65、a 97其余字符都可以顺次推出。2.2 三个关键规律// 规律一数字转整数值intdigit7-0;// 7// 规律二大写与小写相差 32a-A32;// 恒成立A32a;// 大小写转换// 规律三字符比较即 ASCII 码比较09AZaz;// 整体顺序实战利用 ASCII 码规律实现大小写转换下面这段程序利用“大写字母与小写字母相差 32”这一规律实现字符串大小写互换并且对数字、空格、标点等非字母字符原样保留不参与转换#includestdio.h// 利用 ASCII 规律实现大小写互换voidswap_case(char*s){inti0;while(s[i]!\0){charcs[i];// 大写字母A~Z 对应 ASCII 65~90加 32 转成小写if(cAcZ){s[i]c32;}// 小写字母a~z 对应 ASCII 97~122减 32 转成大写elseif(cacz){s[i]c-32;}// 其他字符数字、空格、标点等保持不变else{// 不做任何处理}i;}}intmain(void){chartext[]Hello, C Program 2026!;printf(转换前%s\n,text);swap_case(text);printf(转换后%s\n,text);return0;}运行结果是转换前Hello, C Program 2026! 转换后hELLO, c pROGRAM 2026!这段代码的关键在于判断区间时先判断字母范围再决定加 32 还是减 32不在字母区间内的字符统一走else分支保持原样这样数字、空格和标点都不会被误改。2.3 常用控制字符代码中真正高频出现的控制字符只有三个转义字符ASCII 值含义\n10LF换行\t9HT水平制表符\00NUL字符串结束标志陷阱\0是整数 00是整数 48两者完全不同。判断字符串是否结束用的是\0不是0。3. 附录 BC 语言中的关键字关键字是语言预定义的、具有特殊含义的标识符不能用作变量名、函数名。附录 B 的完整列表可以直接背成一份“禁区清单”。3.1 C89/C90 标准的 32 个关键字按用途分组记忆比死记字母表高效得多类别关键字数据类型charintfloatdoubleshortlongsignedunsignedvoid存储类型autoregisterstaticextern流程控制ifelseswitchcasedefaultforwhiledobreakcontinuegotoreturn构造类型structunionenum其他constvolatiletypedefsizeof3.2 后续标准新增的关键字教材以 C99 为主要参考新增了 5 个关键字了解即可C99 新增inline、restrict、_Bool、_Complex、_ImaginaryC11 新增_Alignas、_Alignof、_Atomic、_Generic、_Noreturn、_Static_assert、_Thread_local3.3 常见易错点intsizeof10;// 错误sizeof 是关键字不能作变量名intmain0;// 错误main 不是关键字但它是程序入口函数名不应占用intclass1;// 合法C 语言没有 class 关键字那是 C 的voidgoto;// 错误goto 是关键字记忆技巧C 的关键字全部是小写IDE 中通常会高亮显示凡高亮且不能命名的就是关键字。4. 附录 C运算符和结合性这是五个附录里最常考、也最易错的部分。核心原则只有一句话先按优先级同级再看结合性。4.1 完整优先级表从高到低优先级运算符名称结合性1()[]-.括号、下标、成员访问左结合2!~---*(type)sizeof单目运算符右结合3*/%乘除取余左结合4-加减左结合5移位左结合6关系运算左结合7!相等判断左结合8按位与左结合9^按位异或左结合10|按位或左结合11逻辑与左结合12||逻辑或左结合13?:条件运算符右结合14-*/%^|赋值运算符右结合15,逗号运算符左结合4.2 结合性速记全表只有三组是右结合其余全部左结合单目运算符第 2 级条件运算符第 13 级赋值运算符第 14 级intabc5;// 右结合等价于 a (b (c 5))int*pa;// 单目右结合* 作用于 p 作用于 a4.3 高频易错优先级对比程序一*p优先级演示#includestdio.hintmain(void){intarr[]{10,20,30};int*parr;// 声明指针 p指向 arr[0]intresult*p;// 等价于 *(p)先取 *p10再让 p 指向 arr[1]printf(arr[0] %d, arr[1] %d, arr[2] %d\n,arr[0],arr[1],arr[2]);printf(表达式 *p 的结果为%d\n,result);// 10printf(执行后 *p 指向的值为%d\n,*p);// 20// 错误理解对照(*p) 会先解引用 arr[0]再把它从 10 自增为 11parr;// 重新指回 arr[0]intwrong(*p);// 自增的是 arr[0] 本身printf((*p) 的结果为%d\n,wrong);// 10先返回旧值printf(执行后 arr[0] 的值为%d\n,arr[0]);// 11return0;}程序二a b c优先级演示#includestdio.hintmain(void){inta6;// 二进制 0110intb3;// 二进制 0011intc2;// 二进制 0010// a b c 等价于 (a b) c因为 优先级高于 // a b 6 3 20110 0011 0010// 2 2 为真所以结果为 1intresult1abc;printf(a b c 的结果为%d\n,result1);// 1// 若按错误结合理解为 a (b c)// b c 即 (3 2) 为 0a 0 0intresult2a(bc);printf(a (b c) 的结果为%d\n,result2);// 0return0;}程序三条件运算符右结合演示#includestdio.hintmain(void){inta1,b3,c2;// 条件运算符从右向左结合// a b ? a : b c ? b : c// 等价于 a b ? a : (b c ? b : c)// a b 为假(1 3)进入括号内b c 为真(3 2)取 b 3intmax1ab?a:bc?b:c;printf(a b ? a : b c ? b : c %d\n,max1);// 3// 显式加括号验证右结合语义intmax2ab?a:(bc?b:c);printf(a b ? a : (b c ? b : c) %d\n,max2);// 3return0;}实战建议拿不准优先级时加括号是最稳妥的做法可读性永远优先于“炫技”。5. 附录 DC 语言常用语法提要附录 D 把全书语法浓缩成一张清单适合考前快速自检。5.1 程序结构#includestdio.h// 预处理指令# 开头行尾无分号intadd(inta,intb);// 函数声明原型intmain(void)// 主函数程序入口{intxadd(3,4);// 语句以分号结尾return0;// 返回语句}intadd(inta,intb)// 函数定义{returnab;}基本骨架一个 C 程序由一个或多个函数组成有且只有一个main函数。5.2 数据类型类型字节数典型 32 位取值范围char1-128 ~ 127short2-32768 ~ 32767int4约 ±2.1×10⁹long4与 int 相同32 位下float4约 6~7 位有效数字double8约 15~16 位有效数字陷阱sizeof返回的类型是size_t字节数随平台而变不要硬背“int 一定是 4 字节”应用sizeof(int)实测。5.3 三种控制结构顺序从上到下逐条执行选择if / else、switch / case循环while、do-while、for。// switch 的贯穿陷阱switch(n){case1:printf(one\n);break;// 漏掉 break 会贯穿到下一个 casecase2:printf(two\n);break;default:printf(other\n);}5.4 数组、指针与函数inta[10];// 数组名代表首元素地址int*pa;// a[i] 等价于 *(a i)voidfunc(intarr[],intn);// 数组形参退化为指针voidfunc(int*arr,intn);// 与上一行等价5.5 结构体与共用体structStudent{charname[20];intage;floatscore;};// 分号不可省略structStudents{Tom,18,95.5};unionData{// 所有成员共享同一块内存inti;floatf;};// sizeof(union) 最大成员的大小6. 附录 EC 库函数速查附录 E 收录了 C 标准库中的常用函数。按头文件分类把“函数名 作用 头文件”三要素记住即可考试和开发中按需查阅。6.1 输入输出函数stdio.h函数作用printf/scanf格式化输出 / 输入putchar/getchar单字符输出 / 输入puts/gets字符串输出 / 输入gets已废弃存在缓冲区溢出风险fopen/fclose打开 / 关闭文件fprintf/fscanf文件格式化读写常见陷阱陷阱一scanf读取变量时漏写。scanf需要传入变量的地址才能写入数据漏写可能导致程序崩溃或写入非法地址。陷阱二使用已废弃的gets读取字符串。gets无法限制输入长度可能造成缓冲区溢出应改用fgets。// 错误写法intn;scanf(%d,n);// 漏写 没有传入 n 的地址charbuf[16];gets(buf);// 危险输入超过 15 个字符会越界// 正确写法intn;scanf(%d,n);// 传入 n 的地址charbuf[16];fgets(buf,sizeof(buf),stdin);// 最多读入 sizeof(buf)-1 个字符6.2 数学函数math.h函数作用备注sqrt(x)平方根x ≥ 0pow(x, y)x 的 y 次幂链接时需-lmfabs(x)浮点绝对值整数用abs()sin/cos/tan三角函数参数为弧度exp(x)/log(x)指数 / 自然对数ceil(x)/floor(x)向上 / 向下取整返回 double常见陷阱陷阱一sqrt(x)对负数开平方。sqrt的定义域是x 0传入负数会返回 NaN且编译、运行时都不一定会报错。陷阱二编译时忘记链接数学库。math.h中的函数大多需要链接libm只有#include math.h还不够。// 错误写法#includemath.h#includestdio.hintmain(void){doublexsqrt(-4.0);// 负数开平方结果是 NaNprintf(%f\n,x);return0;}// 编译命令若写成 gcc main.c可能报 undefined reference// 正确写法#includemath.h#includestdio.hintmain(void){doublex4.0;if(x0)printf(sqrt %f\n,sqrt(x));elseprintf(负数不能开平方\n);return0;}// 编译时加 -lmgcc main.c -lm6.3 字符处理函数ctype.h函数作用返回值isalpha(c)是否字母非 0 为真isdigit(c)是否数字非 0 为真isupper(c)/islower(c)是否大小写非 0 为真toupper(c)/tolower(c)大小写转换转换后字符常见陷阱陷阱一直接把char传给isalpha等函数。当char为有符号类型且值为负时作为参数传给 ctype 函数属于未定义行为应先转换为unsigned char。陷阱二误认为这些函数一定返回1或0。它们只保证“非 0 为真”实际返回值取决于具体实现。// 错误写法charc;if(isalpha(c)){}// c 为负值时行为未定义// 正确写法charc;if(isalpha((unsignedchar)c)){}// 显式转换为 unsigned char6.4 字符串函数string.h函数作用陷阱strlen(s)求长度不含\0时间复杂度 O(n)strcpy(dst, src)复制dst 必须足够大strcat(dst, src)拼接dst 必须够容纳总和strcmp(a, b)比较相等返回 0strchr(s, c)查找字符返回指针或 NULL常见陷阱陷阱一strcpy未检查目标缓冲区大小。strcpy不会检查dst是否装得下src源串过长会导致缓冲区溢出。陷阱二strcmp相等返回0却把它当成“真”来判断相等。if (strcmp(a, b))实际表示“不相等”判断相等应写 0。// 错误写法chardst[5];charsrc[]hello;// 需要 6 字节含 \0strcpy(dst,src);// 溢出dst 只有 5 字节if(strcmp(a,b))// 逻辑反了相等时返回 0不进入分支printf(equal\n);// 正确写法chardst[16];charsrc[]hello;strncpy(dst,src,sizeof(dst)-1);// 限制复制长度dst[sizeof(dst)-1]\0;// 手动补字符串结束符if(strcmp(a,b)0)// 相等才进入分支printf(equal\n);6.5 内存与转换函数stdlib.h函数作用malloc(size)动态分配不初始化calloc(n, size)分配 n 个元素并清零realloc(p, size)调整已分配内存free(p)释放内存atoi(s)/atof(s)字符串转整数 / 浮点数rand()/srand()生成随机数 / 设置种子#includestdlib.h#includetime.hintmain(void){srand((unsigned)time(NULL));// 用时间做种子intnrand()%100;// 0 ~ 99 的随机数return0;}常见陷阱陷阱一malloc分配后忘记free。每次malloc/calloc分配的内存都需要在不再使用时调用free释放否则会造成内存泄漏。陷阱二malloc未检查返回值。分配失败时malloc返回NULL继续解引用会导致程序崩溃。// 错误写法int*pmalloc(10*sizeof(int));p[0]1;// 若 malloc 失败返回 NULL这里解引用会崩溃// 函数结束时未 free造成内存泄漏// 正确写法int*pmalloc(10*sizeof(int));if(pNULL){fprintf(stderr,内存分配失败\n);return1;}p[0]1;free(p);// 不再使用时释放pNULL;// 避免悬挂指针7. 总结与使用建议五个附录各有侧重建议分场景使用写代码查优先级→ 附录 C 常驻手边拿不准就加括号字符与数字混算→ 回忆附录 A 的三个锚点48、65、97命名报错→ 对照附录 B 检查是否撞了关键字考前冲刺→ 用附录 D 逐条自检语法点调用标准函数→ 按附录 E 的头文件分类查找养成“先查手册再写函数”的习惯。把附录当作“字典”而非“必背课文”在一次次查阅中自然内化比考前突击背诵更有效。希望这份整理能帮你把书末那几十页真正用起来。