数据相关

16位操作系统:long:4字节,int:2字节

32位操作系统:long:4字节,int:4字节

64位操作系统:long:8字节,int:4字节

所以最好使用 stdint 内的指定位数的定义(如int8、int32等)保证移植的兼容性

声明

  • 用volatile声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化(告诉编译器对该变量不做优化,都会直接从变量内存地址中读取数据)

static

全局(静态)存储区:分为 DATA 段和 BSS 段。DATA 段(全局初始化区)存放初始化的全局变量和静态变量;BSS 段(全局未初始化区)存放未初始化的全局变量和静态变量。程序运行结束时自动释放。其中BBS段在程序执行之前会被系统自动清0,所以未初始化的全局变量和静态变量在程序执行之前已经为0。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。

  • 在修饰变量的时候,static 修饰的静态局部变量只执行初始化一次,而且延长了局部变量的生命周期,直到程序运行结束以后才释放。
  • static 修饰全局变量的时候,这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是 extern 外部声明也不可以。
  • static 修饰一个函数,则这个函数的只能在本文件中调用,不能被其他文件调用。static 修饰的变量存放在全局数据区的静态变量区,包括全局静态变量和局部静态变量,都在全局数据区分配内存。初始化的时候自动初始化为 0。
  • 不想被释放的时候,可以使用static修饰。比如修饰函数中存放在栈空间的数组。如果不想让这个数组在函数调用结束释放可以使用 static 修饰。
  • 考虑到数据安全性(当程序想要使用全局变量的时候应该先考虑使用 static)。

内存

栈(stack)

核心特性

  • 自动分配与释放:由编译器自动管理,函数调用时分配栈帧,函数返回时自动释放。
  • 后进先出(LIFO)
  • 高速访问
  • 空间有限

存储内容

  • 局部变量:函数内部定义的变量。
  • 返回地址:函数执行完毕后返回的位置。
  • 函数参数:调用函数时传递的参数。
  • 寄存器值:保存调用前的寄存器状态,以便恢复。

优缺点

  • 优点:无需手动管理内存,速度快,不会内存泄漏。
  • 缺点:生命周期固定(函数结束即释放),空间有限。

堆(heap)

核心特性

  • 手动分配与释放:使用malloc/calloc/realloc分配,free释放。
  • 动态生命周期:内存块的生命周期由程序员控制,可跨函数使用。
  • 碎片化问题:频繁分配和释放可能导致内存碎片,降低空间利用率。
  • 慢速访问:需通过指针间接访问,效率低于栈。

存储内容

  • 动态分配的对象:如malloc返回的内存块。
  • 大型数据结构:如数组、链表、树等需要动态调整大小的结构。
  • 跨函数数据:需要在函数调用结束后继续存在的数据。

指针

空指针:指针变量指向内存中编号为0的空间

野指针:指针变量指向非法的内存空间

1
2
3
4
5
//指针指向的值是不可以更改的
const int * p1 = &a;

//指针指向是不可以改的
int * const p2 = &a;

数据集合

共用体(union)

所有成员共用一块内存,大小取决于最大大小成员

枚举(enum)

默认从 0 开始递增

位域(Bit Field)

位域用于结构体中,定义每个字段占用的比特位数;越前面定义的在内存的越高位

1
2
3
4
5
struct Flags {
unsigned int ready : 1; // 1bit
unsigned int error : 1;
unsigned int mode : 2;
};

注意事项:

  • 位域不能取地址(&f.ready 不合法)
  • 字段数值不能超过位数范围(2^n - 1)
  • 与具体编译器实现密切相关(跨平台需小心)

位运算

1
2
3
4
5
#define LED_PIN (1 << 2)

PORT |= LED_PIN; // 置位
PORT &= ~LED_PIN; // 清零
PORT ^= LED_PIN; // 翻转

编译和调试

四阶段

1
2
3
4
gcc -E main.c -o main.i   # 预处理
gcc -S main.c -o main.s # 编译为汇编
gcc -c main.c -o main.o # 汇编为目标文件
gcc main.o -o main # 链接生成可执行文件