C 语言系统 UML 梳理

C 语言由 Dennis Ritchie 于 1969–1973 年在贝尔实验室设计开发,是一种通用的、过程式的编程语言。C 是系统编程的事实标准——Unix/Linux 内核、嵌入式固件、数据库引擎、编程语言运行时均以 C 编写。本文档从行为与结构两个维度,用 UML 图梳理 C 语言的编译系统、运行时和使用者视角。

1. C 语言概述

C 语言是编译型、静态类型的过程式编程语言,设计哲学是"信任程序员"——提供对硬件的直接访问(指针、位操作),不做运行时检查(如数组越界),极高效率。

核心特性

  • 编译型:源码需经过编译才可执行
  • 静态类型:编译时确定类型
  • 过程式:以函数为基本组织单位
  • 指针:直接操作内存地址
  • 预处理器:宏、条件编译、文件包含
  • 标准库精简:约 29 个标准头文件

典型应用领域

  • 操作系统内核(Linux、Windows、macOS)
  • 嵌入式系统(汽车 ECU、IoT 设备)
  • 数据库系统(MySQL、PostgreSQL、SQLite)
  • 编程语言解释器/运行时(Python、Ruby)
  • 高性能计算(科学计算、图形引擎)

2. 行为图(系统做什么)

从使用者视角出发,逐步细化 C 语言编译系统与运行时行为。

2.1 用例图

展示 C 语言的不同使用者及其典型操作场景。

C Language Use Case Diagram
C 语言使用者与用例

2.2 活动图:编译流程

C 源码到可执行文件的四阶段管道(Pipeline):预处理 → 编译 → 汇编 → 链接。

C Compilation Activity Diagram
C 编译四阶段活动图(带泳道)

2.3 序列图:编译管道完整流程

以一个多文件项目为例,展示从源文件到最终可执行文件的完整交互序列。

C Compilation Sequence Diagram
多文件编译序列图

2.4 状态机图:C 程序生命周期

描述一个 C 程序从源代码到运行结束的完整生命周期状态。

C Program Lifecycle State Machine
C 程序生命周期状态机

3. 结构图(系统由什么组成)

从宏观架构到微观细节,拆解 C 语言的静态结构。

3.1 组件图:C 生态系统

展示 C 开发环境的完整生态组件及其依赖关系。

C Ecosystem Component Diagram
C 开发生态系统组件图

3.2 包图:C 标准库结构

C 标准库(libc)的分类组织——头文件按功能划分为不同模块。

C Standard Library Package Diagram
C 标准库头文件包图

3.3 C 程序内存布局

展示 C 程序运行时的虚拟内存地址空间布局(Text / Data / BSS / Heap / Stack)及对应数据存放位置。

C Memory Layout
C 程序运行时内存布局
内存段说明:Text 段存放指令(只读);Data 段存放已初始化全局变量;BSS 段存放未初始化全局变量(启动时零填充);Heap 向上增长,由 malloc/free 管理;Stack 向下增长,存放函数调用帧和局部变量。

3.4 部署图:构建环境

展示一个典型的 C 项目跨平台开发与持续集成部署拓扑。

C Build Environment Deployment Diagram
C 项目构建与 CI 部署图

4. 使用手册

4.1 基本语法

Hello World

#include <stdio.h>

int main(void) {
    printf("Hello, World!\n");
    return 0;
}

变量与基本类型

类型字节(通常)说明示例
char1字符/小整数char c = 'A';
short2短整数short s = 32767;
int4整数int i = 42;
long4/8长整数long l = 100000L;
long long8长长整数long long ll = 9999999LL;
float4单精度浮点float f = 3.14f;
double8双精度浮点double d = 3.1415926;
_Bool1布尔(C99)_Bool flag = 1;

控制流

// if-else
if (x > 0) {
    printf("positive\n");
} else if (x < 0) {
    printf("negative\n");
} else {
    printf("zero\n");
}

// switch
switch (n) {
    case 1: printf("one\n"); break;
    case 2: printf("two\n"); break;
    default: printf("other\n"); break;
}

// for 循环
for (int i = 0; i < 10; i++) {
    printf("%d\n", i);
}

// while 循环
while (n > 0) {
    n--;
}

// do-while
do {
    n++;
} while (n < 100);

4.2 指针与内存

指针是 C 语言最强大也最危险的特征——直接操作内存地址。

int x = 42;
int *p = &x;          // p 指向 x 的地址
printf("%d\n", *p);   // 解引用:输出 42

// 动态内存分配
int *arr = (int *)malloc(10 * sizeof(int));
if (arr == NULL) {
    perror("malloc failed");
    return 1;
}
for (int i = 0; i < 10; i++) arr[i] = i * i;
free(arr);            // 必须手动释放

// 函数指针
int add(int a, int b) { return a + b; }
int (*func_ptr)(int, int) = &add;
printf("%d\n", func_ptr(3, 4));  // 输出 7

// void 指针(泛型指针)
void *vp = &x;
int *ip = (int *)vp;  // 需显式转换

指针与数组

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;          // 数组名退化为指针
// p[2] 等价于 *(p + 2),输出 3

// 指针算术
for (int *q = arr; q < arr + 5; q++) {
    printf("%d ", *q);
}

4.3 结构体与联合体

// 结构体
struct Point {
    int x;
    int y;
};

struct Point p1 = {10, 20};
struct Point *pp = &p1;
pp->x = 30;  // 箭头操作符

// 联合体(所有成员共享同一内存)
union Data {
    int i;
    float f;
    char str[20];
};

// 枚举
enum Color { RED = 0, GREEN, BLUE };
enum Color c = GREEN;

// typedef 别名
typedef struct {
    char name[32];
    int age;
} Person;
Person p2 = {"Alice", 25};

4.4 预处理器

预处理器在编译前对源码进行文本替换,由 # 开头的指令控制。

// 宏定义
#define PI 3.14159
#define SQUARE(x) ((x) * (x))   // 注意括号:避免展开歧义

// 条件编译
#ifdef DEBUG
    printf("Debug mode: x=%d\n", x);
#endif

#if defined(__linux__)
    // Linux 特定代码
#elif defined(_WIN32)
    // Windows 特定代码
#endif

// 头文件卫士(防止重复包含)
#ifndef MYHEADER_H
#define MYHEADER_H
// 头文件内容
#endif

// C++ 兼容
#ifdef __cplusplus
extern "C" {
#endif
// C 函数声明
#ifdef __cplusplus
}
#endif

4.5 编译工具链

GCC 常用命令

# 单文件编译
gcc hello.c -o hello

# 分步编译
gcc -E hello.c -o hello.i       # 仅预处理
gcc -S hello.i -o hello.s       # 编译为汇编
gcc -c hello.s -o hello.o       # 汇编为目标文件
gcc hello.o -o hello            # 链接

# 多文件编译
gcc -c foo.c -o foo.o
gcc -c bar.c -o bar.o
gcc foo.o bar.o -o program

# 常用选项
gcc -Wall -Wextra -g -O2 main.c -o main   # 警告全开 + 调试 + 优化
gcc -std=c11 main.c -o main               # 指定 C11 标准
gcc -I./include -L./lib -lmylib main.c    # 指定头文件/库路径

静态库与动态库

# 静态库(.a / .lib)
gcc -c mylib.c -o mylib.o
ar rcs libmylib.a mylib.o
gcc main.c -L. -lmylib -o program

# 动态库(.so / .dylib / .dll)
gcc -shared -fPIC mylib.c -o libmylib.so
gcc main.c -L. -lmylib -o program

# macOS 动态库
gcc -dynamiclib mylib.c -o libmylib.dylib

调试工具

# GDB
gdb ./program                  # 启动调试
(gdb) break main               # 在 main 设断点
(gdb) run arg1 arg2            # 运行
(gdb) next                     # 单步(不进入函数)
(gdb) step                     # 单步(进入函数)
(gdb) print x                  # 打印变量
(gdb) backtrace                # 查看调用栈
(gdb) watch y                  # 监视变量变化

# Valgrind(内存泄漏检测)
valgrind --leak-check=full ./program

# Address Sanitizer
gcc -fsanitize=address -g main.c -o main
./main

4.6 构建系统

Makefile

CC = gcc
CFLAGS = -Wall -Wextra -g -O2
LDFLAGS =
TARGET = program
OBJS = main.o foo.o bar.o

$(TARGET): $(OBJS)
	$(CC) $(LDFLAGS) -o $@ $^

%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

.PHONY: clean
clean:
	rm -f $(OBJS) $(TARGET)

CMake (CMakeLists.txt)

cmake_minimum_required(VERSION 3.10)
project(MyProject C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

add_executable(program main.c foo.c bar.c)

target_include_directories(program PRIVATE include)
target_link_libraries(program PRIVATE m)  # 链接 libm

5. 语言标准演进

年份标准别名主要新增
1972—K&R C原始版本,Dennis Ritchie 设计
1989C89 / ANSI CANSI X3.159-1989函数原型、void、标准库、const/volatile
1990C90ISO/IEC 9899:1990ISO 采纳的 C89,基本一致
1999C99ISO/IEC 9899:1999内联函数、变长数组、// 注释、long long、_Bool、<stdint.h>
2011C11ISO/IEC 9899:2011_Generic、_Atomic、_Thread_local、匿名 struct/union、bounds-checking 接口(可选)
2018C17ISO/IEC 9899:2018缺陷修复版本,无新特性
2024C23ISO/IEC 9899:2024#embed、nullptr 常量、typeof/typeof_unqual、十进制浮点、constexpr、{} 空初始化器