ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

C语言第四课:关键字不是背单词,先分清break、continue、static与typedef

2026/10/4 8:53:38 拓冰建站 浏览量
C语言第四课:关键字不是背单词,先分清break、continue、static与typedef 我最初想把 C 关键字按用途分类帮助自己记忆。但旧稿没写完整还留下了与 C 无关的 pandas 模板。这里重新接回 第三课的函数和数组不把关键字当一张词汇表而是看看它们怎样改变程序行为。1. 先说明版本“C有32个关键字”不是永远成立常见的“32 个”指 C90 那组关键字。后续标准增加了关键字本文的程序用 C11 模式验证所以不能把旧版数量当成全部现代 C 语法。以下只是便于学习的分组不等于标准中的语法分类用途C90 中对应的关键字基础类型及修饰char、short、int、long、float、double、signed、unsigned、void聚合类型和类型别名struct、union、enum、typedef条件、循环及标签if、else、switch、case、default、for、while、do跳转break、continue、return、goto存储相关auto、static、extern、register类型限定与大小计算const、volatile、sizeof原文把long long单列成一个关键字实际上它是两个long组成的类型说明goto是一个单词不是go to。default是 switch 的标签不是结束语句。printf、main不是关键字#include是预处理指令也不属于这张关键字表。关键字不能拿来给变量或函数命名看到有语法高亮也不意味着它就是关键字。本课不逐项展开先抓几组最容易混淆、又能立刻写程序验证的关键字。2. break、continue、return结束的范围不同#includestdio.hintmain(void){for(inti0;i5;i){if(i1)continue;if(i4)break;printf(loop:%d\n,i);}puts(after-loop);return0;}输出loop:0 loop:2 loop:3 after-loopcontinue在 i1 时跳过本轮剩余语句随后仍执行 for 的递增部分break在 i4 时退出循环但没有退出 main所以还能打印 after-loop。return则结束当前函数。switch 嵌在循环中时break 到底退出谁#includestdio.hintmain(void){for(inti0;i3;i){switch(i){case1:puts(case:1);break;default:puts(default);break;}printf(after-switch:%d\n,i);}return0;}输出default after-switch:0 case:1 after-switch:1 default after-switch:2break退出的是最内层的循环或 switch。这里最内层是 switch不是 for。相反switch 自己不是循环里面的continue会针对包围它的循环。不要把两个关键字笼统记成“跳出”。3. static名字可见范围和对象存在时间分开看下面使用同样的递增逻辑仅改变局部变量的存储方式#includestdio.hstaticintautomatic_next(void){intcount0;returncount;}staticintpersistent_next(void){staticintcount0;returncount;}intmain(void){inta1automatic_next(),a2automatic_next();ints1persistent_next(),s2persistent_next();printf(automatic:%d,%d\n,a1,a2);printf(static:%d,%d\n,s1,s2);return0;}输出automatic:1,1 static:1,2普通局部 count 每次进入函数都会重新初始化静态局部 count 的对象持续存在不因本次调用结束而销毁。它的名字仍然只在这个函数块内可见不能因此在 main 中直接使用。函数前面的static又是另一种用途让该函数名具有内部链接。不能把“static 修饰局部对象”的解释原封不动套到函数上。同样extern通常用于声明其他位置定义的对象或函数不是“自动去别的文件造出变量”。看一个两文件例子counter.cinttotal7;use_counter.c#includestdio.hexterninttotal;intmain(void){printf(total%d\n,total);return0;}将两份源文件一起编译链接输出total7。如果缺少定义 total 的文件声明本身不能替代定义通常在链接阶段报错。register不保证编译器一定把对象放进 CPU 寄存器在本文采用的 C11 规则中不能对 register 声明的对象取地址。入门时不需要把它当作必备性能优化手段。4. typedef 是别名const 与指针一起用时要展开读#includestdio.htypedefint*IntPtr;intmain(void){intvalue10;constIntPtr fixedvalue;*fixed20;printf(value%d\n,value);return0;}输出value20。IntPtr已经是“指向 int 的指针”这个类型const IntPtr限定的是指针对象本身效果相当于int * const不是const int *。int * const p 指针本身不能重绑所指 int 可以修改 const int * p 不能通过 p 修改所指 int指针可以重绑两个“不允许”都要落到具体对象或访问方式上。typedef不创建全新的独立类型也不是简单地按字符进行宏替换。volatile同样不能被简化成“线程安全”或“保证原子操作”线程同步需要另外的机制。本课先把这个误区拦住不展开并发教程。5. sizeof数组参数不是完整数组#includestdio.hstaticsize_tsum(constintvalues[],size_tcount){size_ttotal0;for(size_ti0;icount;i)total(size_t)values[i];returntotal;}intmain(void){intvalues[]{1,2,3};size_tcountsizeofvalues/sizeofvalues[0];printf(count%zu, sum%zu\n,count,sum(values,count));return0;}输出count3, sum6。这里在数组定义所在位置计算长度再显式传给函数。形参的数组写法会调整为指针不能在函数里照搬同一个 sizeof 比值来推断长度。这个求和只是针对本例非负小整数不是任意整数通用的求和接口。sizeof的结果类型是 size_tC11 中涉及 VLA 的情形可能需要运行时计算不能把它概括成“永远在编译时完成”。6. 这次怎样验证完整程序分别编译核对输出extern 例子既测双文件成功也测缺少定义时的预期链接失败另外单独验证 const 指针重绑和 register 取地址的非法例子不把故意错误混进正常程序。版本语义参考 C11 委员会草案 N1570 的关键字、存储类别与类型定义章节。这一课我希望记住的是三个问题关键字作用在哪个对象上改变的是哪种属性结束的是哪一层执行。能回答这三个问题比先把词汇表背下来更能帮助自己读代码。