C语言教程

以 ISO C17 为核心教学基线,覆盖基础语法、函数、数组与字符串、指针、聚合类型、动态内存、预处理与多文件、标准库、文件 I/O、工程实践与综合实验。

C 语言程序设计学习路线与教程导航

本章覆盖:课程定位:以标准 C 语言、内存模型、指针和工程基础为主线;建议教学基线:ISO C17;C23 作为明确标注的现代特性补充;核心学习路径:语法 → 函数 → 数组/字符串 → 指针 → 聚合类型 → 动态内存 → 预处理/多文件 → 标准库/文件 → 工程与系统视角;C 语言与数据结构、操作系统、嵌入式、Linux/POSIX、C++ 的课程边界;学习方法:手写、编译器警告、调试器、Sanitizer、代码阅读与错误复现;学完后的去向:数据结构与算法、计算机组成/操作系统、嵌入式、Linux 系统编程、C++。

C 语言起源、定位、标准演进与应用边界

本章覆盖:C 语言、Dennis Ritchie、Bell Labs 与 UNIX 的历史关系;C 的过程式、编译型、通用系统编程语言定位;C89/C90、C99、C11、C17、C23 的标准演进;ISO C 标准与 GCC/Clang/MSVC/嵌入式编译器实现的区别;Hosted environment 与 Freestanding environment 的基本区别;C 在操作系统、嵌入式、数据库、网络库、编译器等领域的典型角色;本课程为什么以 C17 为核心、C23 作为补充。

字面量、变量、初始化与赋值

本章覆盖:整数、浮点、字符、字符串字面量;变量声明;初始化与赋值的区别;未初始化自动变量的风险;块内变量与可见范围初步;常见转义字符。

函数声明、函数定义与函数原型

本章覆盖:函数声明;函数定义;函数原型 prototype;返回类型与参数类型;调用前可见声明;函数签名在 C 中的实际组成与 C++ 概念区别。

一维数组:声明、初始化与访问

本章覆盖:数组类型;数组长度;初始化列表;未显式初始化元素的规则;下标访问;数组连续存储的标准语义;数组越界是未定义行为。

C 对象生命周期与“栈/堆/静态区”工程模型

本章覆盖:标准中的 object、storage duration、lifetime;automatic/static/allocated storage duration;工程实现中常说的 stack / heap / data / bss / code;“局部变量一定在栈上”等说法不是 ISO C 语言保证;对象生命周期结束后继续访问是未定义行为;指针有效性依赖所指对象生命周期。

C 预处理器与 preprocessing token

本章覆盖:预处理发生在正式翻译之前;#include;#define;#if 等预处理指令;宏展开与 C 语法解析是不同阶段;预处理器本质上不理解普通 C 类型系统。

C 标准库总览与 Hosted / Freestanding 边界

本章覆盖:标准库通过头文件暴露接口;stdio.h、stdlib.h、string.h、stdint.h 等主干头文件;Hosted implementation 提供完整标准库要求;Freestanding implementation 只保证标准规定的子集;标准库、POSIX API、Windows API 不是同一层;第三方库不属于 C 标准库。

Undefined、Unspecified、Implementation-defined 与诊断

本章覆盖:undefined behavior;unspecified behavior;implementation-defined behavior;constraint violation 与诊断;可移植程序不能依赖未被标准保证的偶然行为;编译成功不等于程序没有未定义行为。

C 基本数据类型与 sizeof

本章覆盖:char、short、int、long、long long;signed 与 unsigned;float、double、long double;_Bool / bool 的版本差异;void;sizeof 运算符;类型大小由实现决定,不能死背所有平台固定字节数。

参数传递:C 只有值传递

本章覆盖:实参与形参;标量值的副本;指针值作为参数时仍然是值传递;通过指针修改调用者对象;数组形参调整为指针类型;“引用传递”不是 C 的语言机制。

数组遍历、长度计算与边界管理

本章覆盖:基于下标遍历;sizeof(array) / sizeof(array[0]);长度只在数组本体可见位置可直接推导;循环边界;哨兵值与显式长度;API 中传递 pointer + length 的常见设计。

malloc:动态分配与 sizeof 惯用写法

本章覆盖:malloc;返回 void *;分配失败返回空指针;sizeof *ptr 的惯用写法;分配得到的存储内容初始值不确定;C 中不需要强制转换 malloc 返回值。

对象式宏与符号常量

本章覆盖:object-like macro;宏常量;括号与替换文本;宏没有类型;优先使用 enum / const object / inline function 的场景;宏适合条件编译和编译期配置。

stdio.h:标准流、缓冲与格式化 I/O

本章覆盖:stdin、stdout、stderr;printf / fprintf;scanf / fscanf 的定位;字符 I/O 与行 I/O;I/O 缓冲的基础概念;fflush 的适用边界;错误返回值检查。

有符号/无符号、整数溢出与比较陷阱

本章覆盖:unsigned 模运算;signed integer overflow 是未定义行为;有符号与无符号比较;size_t 与 int 混用;边界检查先后顺序;整数溢出可能转化为内存安全漏洞。

安全 CSV / 分隔文本解析器

本章覆盖:fgets 行读取;缓冲区边界;字段切分;动态字符串/数组;非法输入处理;结构体建模;结果写回文件。

GCC / Clang 环境、标准模式与常用编译选项

本章覆盖:GCC 与 Clang 的定位;gcc/clang 的最小编译命令;-std=c17 与标准模式;-Wall、-Wextra、-Wpedantic 等警告选项;-o、-c、-E、-S 的作用;调试构建 -g 与优化级别 -O0/-O2 的基本认识;GNU 扩展与 ISO C 的边界。

整数范围、字符类型与固定宽度整数

本章覆盖:limits.h 中的整数范围宏;CHAR_BIT;char / signed char / unsigned char 的区别;char 的 signedness 由实现决定;stdint.h 的 int8_t/int32_t/uint32_t 等类型;int_leastN_t、int_fastN_t 的概念;size_t、ptrdiff_t 的基础定位。

返回值、void 与多结果输出

本章覆盖:return;void 函数;返回标量值;返回结构体;使用输出参数返回多个结果;不能返回已经结束生命周期的局部对象地址。

空指针、无效指针、野指针与悬空指针

本章覆盖:null pointer 的语义;NULL;未初始化指针;悬空指针 dangling pointer;释放后指针;返回局部对象地址;不能把“地址看起来非 0”当作有效性证明。

calloc:数组分配与零字节初始化

本章覆盖:calloc(count, size);分配数组空间;字节置零的语义;“全 0 位模式”等于所有类型数值 0 的边界;count * size 溢出风险由实现/函数接口处理程度需要核对;malloc + memset 与 calloc 不应机械视为完全等价。

函数式宏、重复求值与副作用陷阱

本章覆盖:function-like macro;参数和整体表达式加括号;宏参数可能重复求值;++/函数调用等带副作用实参风险;宏与 inline 函数的差异;不要用宏制造隐藏控制流。

const、volatile、restrict 的语义与误区

本章覆盖:const 表达通过特定左值不可修改;volatile 用于可能被实现之外因素改变或具有特殊副作用的访问语义;volatile 常见于内存映射 I/O、信号/中断相关对象;volatile 不提供原子性,也不是通用线程同步方案;restrict 用于表达指针别名约束并帮助优化;违反 restrict 契约可能导致未定义行为。

动态数组库 mini-vector

本章覆盖:void * 或明确元素类型两种设计路线;length / capacity;malloc / realloc / free;append / remove / get;所有权约定;错误码;Sanitizer 验证。

C 程序基本结构、main 函数与程序终止

本章覆盖:include 指令、声明、函数定义与 main;int main(void);int main(int argc, char *argv[]);return 0 与 EXIT_SUCCESS / EXIT_FAILURE;命令行参数 argc / argv 的基础语义;标准 C 对 main 形式的边界。

浮点类型、精度与浮点比较

本章覆盖:float/double/long double;float.h;有限精度与舍入误差;浮点数不能简单等同于十进制实数;直接使用 == 比较浮点数的风险;NaN、Infinity 只建立基础认知。

递归、调用栈与递归边界

本章覆盖:递归定义;基线条件;递归调用过程;每次函数调用拥有独立自动对象;递归深度与栈空间风险;递归与迭代的选择。

数组到指针转换与函数形参数组调整

本章覆盖:大多数表达式中的 array-to-pointer conversion;sizeof、& 等典型不发生转换的场景;函数参数中的 T a[] 与 T *a;数组长度信息不会随指针自动传递;数组对象与指针对象不是同一类型。

指针算术、数组边界与 one-past

本章覆盖:p + n / p - n;指针差 ptrdiff_t;只有同一数组对象内的指针算术具有规定语义;one-past pointer 可以形成但不能解引用;关系比较的边界;越界指针算术的未定义行为风险。

结构体作为参数和返回值

本章覆盖:按值传递结构体;通过指针传递结构体;const struct T *;返回结构体值;大对象复制成本是工程考虑而非语言正确性问题;接口选择基于所有权与修改意图。

realloc:扩缩容、失败语义与安全更新

本章覆盖:realloc;可能原地调整,也可能移动;成功后旧指针失效的条件;失败时原块保持有效;使用临时指针避免丢失原内存;size 为 0 的版本/实现边界;动态数组扩容。

条件编译与平台配置

本章覆盖:#if / #ifdef / #ifndef;#elif / #else / #endif;defined;功能开关;平台差异;Debug/Release 条件;条件编译过度会降低可维护性。

对象表示、字节序与大小端

本章覆盖:object representation;unsigned char 可观察对象字节表示;大端/小端属于实现/硬件特征,不是 ISO C 固定规定;整数序列化不能直接假设本机端序;union/指针转换判断端序的可移植性边界;网络/文件格式应显式定义字节序。

注释、关键字、标识符与代码命名规范

本章覆盖:// 与 /* ... */ 注释;关键字与标识符;标识符合法规则;保留标识符的基本边界;变量、函数、类型、宏、常量的命名习惯;可读代码与高价值注释。

字符、转义序列与文本编码边界

本章覆盖:字符常量与字符数组中的字符;常见转义序列;'0' 与 0 的区别;ASCII 作为常见字符编码子集;C char 与文本编码不是同一概念;UTF-8 字节序列与 char 数组的基本关系;多字节字符/宽字符不进入核心深入。

C 字符串、字符数组与字符串字面量

本章覆盖:C 字符串是以 '\0' 结尾的字符序列;char 数组保存字符串;字符串字面量;字符串字面量不可修改的可移植性规则;数组容量与字符串长度的区别;缺失终止空字符的风险。

数组与指针:相似性、转换与本质区别

本章覆盖:数组名在表达式中的转换;指针变量可以重新赋值,数组对象不可以;sizeof(array) 与 sizeof(pointer);&array 与 array 的类型区别;指针遍历数组;数组不是“本质上就是指针”。

结构体内存对齐、填充与 offsetof

本章覆盖:alignment 对齐概念;padding 填充;结构体成员顺序影响 sizeof 的常见实现现象;_Alignof / alignof 的版本边界;offsetof;不要把某个平台对齐规则写成 ISO C 永恒保证;协议/文件格式不能直接依赖未经定义的结构体内存布局。

free、所有权与资源释放责任

本章覆盖:free;只能释放合适的动态分配块;每个成功所有权通常对应一次释放责任;double free;free 后原指针成为悬空指针;将本地变量设为 NULL 不能自动修复其他别名指针;明确 ownership 所有权转移。

#include、头文件与接口声明

本章覆盖:系统头文件与用户头文件;头文件放公开声明;源文件放实现;头文件应自包含;include 顺序与依赖;不要依赖其他头文件间接 include。

ctype.h:字符分类与大小写转换

本章覆盖:isalpha / isdigit / isspace;isalnum 等分类函数;tolower / toupper;传入值必须满足 unsigned char 或 EOF 的契约;ASCII 假设与 locale 边界。

结构体对齐、ABI 与二进制接口边界

本章覆盖:alignment 与 padding 回顾;ABI 决定具体调用约定和布局细节;#pragma pack 属于编译器扩展;跨编译器/平台共享二进制结构需要明确协议;不能把 sizeof(struct) 当作稳定网络协议长度;offsetof 可用于受控布局检查。

翻译单元、声明、定义与符号的初步认识

本章覆盖:Translation Unit(翻译单元);声明 declaration 与定义 definition;函数原型的作用;对象与函数符号;头文件不是独立编译单元;多文件程序为何需要声明与链接。

隐式转换、整数提升与常用算术转换

本章覆盖:整数提升 integer promotions;usual arithmetic conversions;有符号与无符号混合运算;窄化转换;浮点与整数之间的转换;显式强制类型转换;转换导致截断、回绕或精度损失的风险。

存储期:automatic、static、thread 与 allocated

本章覆盖:automatic storage duration;static storage duration;thread storage duration 的标准概念;allocated storage duration;对象生命周期 lifetime;静态存储期对象的零初始化;标准存储期与“栈/堆”实现术语的区别。

fgets、puts 与安全的行输入

本章覆盖:fgets 的缓冲区大小参数;fgets 可能保留换行符;检测并移除换行符;输入行超过缓冲区时的处理意识;puts;为什么教学与工程中常优先 fgets 而非无界字符串读取。

指针数组与数组指针

本章覆盖:T *a[N];T (*p)[N];括号对声明解析的影响;二维数组行指针;指针数组处理字符串列表;sizeof 与步长差异。

联合体 union 与共享存储

本章覆盖:union 成员共享存储;union 大小与对齐;一次存储一个活动表示的基本思路;使用 union 节省存储的场景;类型重解释涉及的可移植性边界;tagged union 思想。

动态数组与容量管理

本章覆盖:length 与 capacity;按元素个数分配;扩容策略;realloc 安全模式;插入/删除导致元素移动;动态数组是理解真实容器实现的基础。

Include Guard 与重复包含

本章覆盖:#ifndef / #define / #endif 头文件保护;为何一个头文件可能被多次间接包含;重复声明与重复定义的区别;#pragma once 属于常见扩展而不是 ISO C 核心机制。

math.h 与常用数学函数

本章覆盖:sqrt、pow、fabs;floor、ceil、round;三角函数基础;isnan / isinf 的基础;浮点错误与 errno/fenv 的深入边界;部分平台链接 math library 的工具链要求。

安全输入、缓冲区长度与边界驱动编程

本章覆盖:所有数组/缓冲区操作明确 capacity;优先 length-aware API;fgets + 解析;字符串复制前验证目标空间;处理截断、EOF、非法输入;不要依赖 gets 等不安全/已移除接口。

二进制记录文件与显式序列化工具

本章覆盖:结构体作为内存模型;显式字段编码;固定宽度整数;端序处理;fread / fwrite;避免直接 dump struct 的可移植性问题;版本字段与校验。

算术、关系与逻辑运算符

本章覆盖:+、-、*、/、%;整数除法;<、<=、>、>=、==、!=;&&、||、!;短路求值;真假值:0 与非 0;运算符优先级与括号。

static、extern 与链接属性

本章覆盖:external linkage;internal linkage;no linkage;文件作用域 static 对象;static 函数;extern 声明;跨翻译单元共享符号。

string.h:长度、复制、拼接与比较

本章覆盖:strlen;strcpy / strncpy 的语义与风险;strcat / strncat 的语义与风险;strcmp / strncmp;目标缓冲区容量必须由程序员管理;n 系列函数不是自动安全方案。

二级指针与多级间接访问

本章覆盖:T **;指向指针的指针;通过二级指针修改调用者指针;动态创建对象并通过输出参数返回;argv 与 char **;多级指针并不等价于二维数组。

枚举 enum 与命名整数常量

本章覆盖:enum 定义;枚举常量;枚举对象;enum 用于状态、类型标签;枚举底层表示的实现相关性;避免魔法数字。

动态结构体、字符串与对象图

本章覆盖:动态分配单个结构体;结构体内部动态成员;深释放与嵌套所有权;动态字符串;对象图中谁拥有谁;部分构造失败时的清理。

回调驱动的命令处理器

本章覆盖:函数指针;命令表结构体数组;回调注册;字符串匹配;参数解析;模块化命令实现;模拟事件分发机制。

位运算与位级数据处理

本章覆盖:&、|、^、~;<< 与 >>;掩码 mask;置位、清位、翻转与读取位;unsigned 类型更适合位运算的原因;移位的边界与未定义/实现定义风险;位操作在标志位和寄存器中的典型意义。

声明、定义与 tentative definition

本章覆盖:对象声明与对象定义;函数声明与函数定义;tentative definition;重复定义导致的链接问题;头文件里为什么通常不放普通外部对象定义;extern 声明与单一定义原则的 C 语境。

字符串查找、分割与转换

本章覆盖:strchr / strrchr;strstr;strtok 的状态性与修改输入行为;strspn / strcspn 的基础用途;atoi 的错误处理缺陷;数值解析优先 strtol / strtod。

const 与指针组合

本章覆盖:pointer to const;const pointer;const pointer to const;从右向左/基于声明器结构阅读;const 限制的是通过该左值进行修改的能力;接口中使用 const 表达只读契约。

typedef 与类型别名

本章覆盖:typedef 不是创建新运行时类型;为 struct 建立别名;指针 typedef 的可读性与陷阱;函数指针 typedef;固定宽度整数类型本质也是 typedef 名称;API 设计中的类型命名。

多文件工程:.h / .c 分层与模块化设计

本章覆盖:public header;private implementation;一个功能模块的 .h + .c;函数声明与实现分离;内部辅助函数使用 static;减少全局可见符号;模块依赖方向。

assert、errno、perror 与 strerror

本章覆盖:assert 用于程序员假设,不用于普通用户输入校验;NDEBUG;errno 的读取时机;函数返回值优先判断错误;perror;strerror;错误码、errno 与返回值的协作。

内存安全故障实验室

本章覆盖:故意制造数组越界;Use-After-Free;double free;整数溢出导致错误分配;悬空指针;使用编译器 warning、GDB、ASan/UBSan 定位;为每类错误写根因报告。

inline 函数与宏函数的边界

本章覆盖:inline 的语言语义;inline 不等于强制内联;static inline 的常见头文件用法;inline 与外部定义的复杂边界只建立工程认知;inline 函数与函数式宏的类型安全差异。

void * 与通用对象指针

本章覆盖:void * 可保存对象指针;对象指针与 void * 的标准转换;void * 不能直接解引用;malloc 返回 void *;通用容器/回调接口中的 void *;函数指针与 void * 不能按对象指针规则混用。

位域 bit-field 与硬件/协议边界

本章覆盖:位域语法;节省表示空间的用途;位域类型与布局限制;位域排列顺序和对齐具有实现相关性;不要直接把位域内存布局当作可移植网络/文件协议格式;嵌入式寄存器映射使用时必须理解编译器/ABI 约束。

分配尺寸计算、整数溢出与安全容量检查

本章覆盖:count * sizeof(element);size_t;乘法溢出会导致少分配;扩容前检查最大容量;SIZE_MAX;不要把 signed int 直接当所有容量/长度类型;C23 checked integer arithmetic 作为补充了解。

编译、目标文件、静态库与链接基础

本章覆盖:分别编译多个 .c;目标文件 .o/.obj;链接多个目标文件;undefined reference / duplicate symbol;静态库 archive 的基础概念;链接顺序的工具链相关性;构建系统深入留给工程化课程。

AddressSanitizer 与 UndefinedBehaviorSanitizer

本章覆盖:AddressSanitizer 检测越界、Use-After-Free 等内存错误;UndefinedBehaviorSanitizer 检测部分未定义行为;-fsanitize=address;-fsanitize=undefined;Sanitizer 不是形式证明,也不能覆盖所有输入;修根因而不是忽略报告。

printf 与格式化输出

本章覆盖:printf 基本使用;%d、%u、%ld、%lld;%f、%e、%g;%c、%s、%p;字段宽度与精度;格式说明符与参数类型必须匹配;inttypes.h 的 PRI 宏基础。

可变参数函数与 stdarg.h

本章覆盖:省略号 ... 参数;va_list;va_start;va_arg;va_end;默认参数提升;可变参数缺乏类型信息带来的风险。

数组与字符串常见问题求解

本章覆盖:最大值、最小值、统计;数组反转与元素移动;字符串长度与反转的手写思路;字符频次统计;子串与分隔符解析;边界、容量与终止符检查。

Flexible Array Member 与变长结构体对象

本章覆盖:结构体末尾 flexible array member;sizeof 不包含灵活数组实际元素空间;按 sizeof(struct)+N*sizeof(element) 分配;通过一个分配块组织头部与尾部数据;容量和长度必须另行维护;适用场景与越界风险。

标准 C、编译器扩展、平台 API 与 C23 现代特性边界

本章覆盖:ISO C 与 GNU C 扩展;POSIX / Linux API 不属于 ISO C;Windows API 不属于 ISO C;嵌入式编译器可能提供硬件扩展;C23 的 nullptr、bool/true/false、typeof、constexpr、attributes、#embed 等只作明确版本补充;课程核心不因新标准出现而静默改变 C17 语义。

scanf 与格式化输入的规则和陷阱

本章覆盖:scanf 的返回值;格式字符串与地址参数;%d、%lf、%c、%s;空白字符匹配规则;%s 的缓冲区越界风险;读取失败后输入流状态;复杂输入优先 fgets + 解析的工程原则。

指针参数、输出参数与接口设计

本章覆盖:通过 T * 修改一个对象;通过 T ** 修改一个指针;pointer + length;pointer + capacity;NULL 是否允许由接口契约明确;返回错误码 + 输出参数的常见 C API 设计。

条件分支:if、else 与 switch

本章覆盖:if / else if / else;嵌套分支;switch / case / default;break 与 fall-through;case 标签必须满足的常量表达式要求;条件表达式的可读性。

复杂声明阅读与指针安全检查

本章覆盖:声明器 declarator 的基本阅读方法;指向函数的指针;返回指针的函数;数组与函数指针组合;先 typedef 再简化复杂声明的工程方式;检查生命周期、边界、空指针和所有权。

文件位置、EOF、错误状态与缓冲

本章覆盖:fseek / ftell / rewind;SEEK_SET / SEEK_CUR / SEEK_END;feof;ferror;clearerr;为什么不能用 while (!feof(fp)) 作为通用读取模式;缓冲、flush 与 close。

表达式求值顺序、副作用与未定义行为入门

本章覆盖:副作用 side effect;求值顺序不等于代码书写顺序;sequenced before 的基础概念;同一对象在无序副作用中的风险;未定义行为 undefined behavior 的初步概念;不要通过编译器“当前碰巧输出什么”推断语言保证。