C语言基础阶段
一、C语言关键字
1. static(静态修饰符)
两种用法:
// 1. 限制全局变量和函数的作用域(只能在当前文件使用) static int global_var = 10; // 只能在当前文件访问 static void func(void) { // 只能在当前文件调用 // ... } // 2. 延长局部变量的生命周期(只会初始化一次) void test(void) { static int count = 0; // 只在第一次调用时初始化为0 count++; // 每次调用count都会保持上次的值 printf("%d\n", count); } // 调用test()三次的输出: // 第一次: 1 // 第二次: 2 // 第三次: 32. extern(外部声明)
作用:声明一个外部符号(函数/全局变量)可以在本文件中使用
// file1.c int global_value = 100; // 定义全局变量 // file2.c extern int global_value; // 声明外部变量,告诉编译器在别的文件中 extern void external_func(void); // 声明外部函数 void test(void) { printf("%d\n", global_value); // 可以使用file1.c中的变量 }3. const(常量修饰符)
作用:修饰符号为只读(99%情况下指的是变量)
const int MAX_SIZE = 100; // MAX_SIZE是只读的 // MAX_SIZE = 200; // 错误!不能修改const变量 // 指针相关的const const char *p1; // p1指向的内容是常量(不能通过p1修改) char * const p2; // p2本身是常量(不能修改p2指向的地址) const char * const p3; // p3和它指向的内容都是常量
4. volatile(易失性修饰符)
作用:每次都从内存地址中访问数据,防止编译器优化
volatile int flag = 0; // 告诉编译器不要优化这个变量 // 常用于: // 1. 多线程共享的变量 // 2. 硬件寄存器 // 3. 中断服务程序修改的变量
二、数组、指针、函数
1. 指针相关概念
// 基本指针 int a = 10; int *p = &a; // p是指向int的指针 // 函数指针:指向函数的指针 int (*pfun)(int); // pfun指向返回int,参数为int的函数 // 对应的函数:int func(int a); // 指针函数:返回值类型为指针的函数 int *func(int a); // func函数返回int指针 // 数组指针:指向数组的指针 int (*parr)[10]; // parr指向包含10个int的数组 // 对应的数组:int arr[10]; // 指针数组:元素类型为指针的数组 int *arr[10]; // arr是包含10个int指针的数组 // 函数指针数组:元素类型为函数指针的数组 int (*func_arr[10])(int); // 包含10个函数指针的数组
三、结构体和内存对齐
1. 结构体定义
// 定义一个新类型 struct Student { char name[20]; int age; float score; }; // 使用 struct Student stu1; stu1.age = 20; // 或者使用typedef typedef struct { char name[20]; int age; } Person; Person p1; // 可以直接使用2. 内存对齐
// 设置内存对齐方式 #pragma pack(1) // 按1字节对齐 struct Test1 { char a; int b; }; // 大小为5字节 #pragma pack(4) // 按4字节对齐(默认) struct Test2 { char a; int b; }; // 大小为8字节(有3字节填充) // Linux中的对齐方式 struct Test3 { char a; int b; } __attribute__((packed)); // 按1字节对齐3. 共用体(联合体)
union Data { int i; float f; char str[20]; }; // 所有成员共用同一段内存 // 大小为最大成员的大小(20字节)四、预处理和宏定义
1. 宏定义
// 简单宏 #define PI 3.14159 // 带参宏 #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define SQUARE(x) ((x) * (x)) // 使用 int max_val = MAX(10, 20); int square_val = SQUARE(5); // ((5) * (5))
2. 条件编译
// 基本条件编译 #if 1 // 这部分代码会被编译 #else // 这部分不会 #endif // 防止头文件重复包含 #ifndef MY_HEADER_H #define MY_HEADER_H // 头文件内容 #endif // 根据平台编译 #ifdef LINUX // Linux专用代码 #elif defined(WINDOWS) // Windows专用代码 #endif
五、C语言编译过程
4个阶段:
main.c → main.i → main.s → main.o → mainapp(bin) ↓ ↓ ↓ ↓ ↓ 源代码 预处理后 汇编代码 目标文件 可执行程序
1. 预处理(Preprocessing)
处理内容:
去掉注释
宏替换
头文件展开
特殊符号处理
// main.c 中的代码 printf("%s %d i = %d\n", __FILE__, __LINE__, i); // 预处理后变成(假设在test.c第5行) printf("test.c 5 i = %d\n", i);生成文件:.i文件(符合C语法的纯文本文件)
2. 编译(Compilation)
作用:C语言源文件转换为汇编文件
进行C语言的语法分析
检查语法错误
生成中间代码
生成文件:.s文件(汇编文件)
3. 汇编(Assembly)
作用:汇编程序转换为二进制机器码
将汇编指令转换为机器指令
生成可重定位的目标文件
生成文件:.o文件(目标文件,二进制机器码)
4. 链接(Linking)
作用:将所有.o文件链接为可执行程序
解决符号引用(函数调用、变量引用)
合并各个模块
添加启动代码
生成文件:可执行二进制文件
注意:1-3阶段每个.c文件独立处理,n个.c文件会生成n个.o文件
六、文件编程
1. 两种IO接口
// 标准IO(有缓冲区,效率高) #include <stdio.h> FILE *fp = fopen("file.txt", "r"); fread(buffer, 1, size, fp); fclose(fp); // 系统IO(无缓冲区,效率低,仅Linux) #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int fd = open("file.txt", O_RDONLY); read(fd, buffer, size); close(fd);2. 缓冲类型
无缓冲:立即读写(如系统IO)
行缓冲:遇到换行符或缓冲区满时刷新(如终端输出)
全缓冲:缓冲区满时刷新(如文件操作)
3. 性能测试
// 测试磁盘IO效率 // 方法1:每次写1字节,写1000次(效率最低) // 方法2:每次写100字节,写1000次(效率中等) // 方法3:每次写1000字节,写1次(效率最高) // 文件空洞:文件中间有未写入数据的部分 lseek(fd, 1024, SEEK_SET); // 跳到1024字节位置 write(fd, "data", 4); // 创建文件空洞
七、进程与线程
1. 进程空间布局(4GB)
0xFFFFFFFF (4GB) ┌─────────────────┐ │ Kernel │ │ (内核空间) │ 0xC0000000 (3GB) ├─────────────────┤ ← 内核空间与用户空间分界线 │ │ │ Stack │ ← 线程独享(每个线程有自己的栈) │ (栈) │ ├─────────────────┤ │ Memory Map │ ← 库函数地址映射区 │ (映射) │ ├─────────────────┤ │ Heap │ ← 动态分配内存(需要手动管理) │ (堆) │ ├─────────────────┤ │ BSS │ ← 未初始化的全局/静态变量 │ │ ├─────────────────┤ │ Data │ ← 初始化的全局/静态变量 │ (数据段) │ ├─────────────────┤ │ Code │ ← 程序代码(二进制指令) │ (代码段) │ 0x00000000 (0GB) └─────────────────┘
2. 各段说明
Code段:存储程序二进制代码(只读)
Data段:存储初始化为非0的全局变量和静态变量
BSS段:存储未初始化或初始化为0的全局变量和静态变量
Heap段:用户自行管理的动态内存(malloc/free)
Stack段:线程独享,存储局部变量、函数参数、返回地址
Map段:存储共享库函数的地址映射
Kernel段:内核空间,用户程序不能直接访问
3. 进程 vs 线程
| 特性 | 进程 | 线程 |
|---|---|---|
| 空间大小 | 4GB(用户空间3GB) | 共享进程空间 |
| 栈 | 每个进程有自己的栈 | 每个线程有独立栈(8MB) |
| 创建效率 | 低(需要复制资源) | 高(共享资源) |
| 通信复杂度 | 复杂(需要IPC机制) | 简单(共享内存) |
| 稳定性 | 高(相互隔离) | 低(相互影响) |
| 资源开销 | 大 | 小 |
九、网络基础
1. 网络模型对比
OSI七层模型 TCP/IP四层模型 7. 应用层 应用层 6. 表示层 5. 会话层 4. 传输层 传输层 3. 网络层 网络层 2. 数据链路层 网络接口层 1. 物理层
2. TCP vs UDP
| 特性 | TCP | UDP |
|---|---|---|
| 连接 | 有连接 | 无连接 |
| 通信模式 | 一对一 | 一对多/多对多 |
| 数据传输 | 数据流 | 数据报(包) |
| 可靠性 | 高(有应答机制) | 低 |
| 效率 | 低 | 高 |
| 特性 | 拥塞控制、重传机制 | 无控制机制 |
3. 数据包封装过程
应用层数据 ↓ +----------------+ | TCP/UDP头部 | ← 传输层 +----------------+ | IP头部 | ← 网络层 +----------------+ | 以太网头部 | ← 链路层 | 以太网校验 | +----------------+ 实际在以太网中传输的数据帧
4. 各层头部关键字段
IP头部(网络层):
32位源IP地址
32位目的IP地址
8位协议类型(TCP=6, UDP=17)
16位总长度
TCP头部(传输层):
16位源端口
16位目的端口
32位序列号(数据包序号)
32位确认号(应答序号)
6位标志位(SYN、ACK、FIN等)
16位窗口大小(接收端缓冲区大小)
以太网头部(链路层):
6字节目的MAC地址
6字节源MAC地址
2字节类型(0800=IP, 0806=ARP)
46-1500字节数据
4字节CRC校验