嵌入式 C/C++ 核心考点:内存管理、多态与系统调用
在嵌入式开发中,对底层机制的理解往往决定了系统的稳定性。今天我们来梳理几个高频出现的 C/C++ 知识点,涵盖变量存储、面向对象特性以及进程控制,希望能帮你把基础打得更牢。
一、变量与内存管理
全局变量和静态变量的区别
很多初学者容易混淆这两者,其实核心差异在于作用域和生命周期。
- 作用域:全局变量对整个程序可见,而局部变量仅限于当前函数或代码块内。
- 存储位置:全局变量(包括静态全局和静态局部)分配在全局数据区(静态存储空间),而普通局部变量通常分配在栈区。
- 生命周期:全局变量随程序启动创建,随程序结束销毁;局部变量则随着函数调用入栈而出栈,存在时间较短。
- 使用方式:全局变量可在程序各部分共享,局部变量只能在定义它的范围内访问。
全局变量能否定义在头文件中?
可以,但要注意规则。如果在多个 .c 文件中包含同一个头文件,且都在其中声明了同名全局变量,链接时会报错。正确的做法是:在头文件中声明 extern,或者在其中一个 .c 文件中定义并赋初值,其他文件通过 extern 引用。如果都定义为 static,则每个文件都有独立的副本,不会冲突,但这通常不是我们想要的效果。
局部变量能否和全局变量重名?
能。当局部变量与全局变量同名时,局部变量会屏蔽全局变量。在函数内部引用该名字时,编译器优先匹配局部变量。有些编译器甚至允许在同一个函数的不同循环体内定义同名的局部变量,它们的作用域仅限于各自的循环体。
二、C++ 面向对象特性
为什么析构函数必须是虚函数?
这是为了防止内存泄漏。当你通过基类指针指向一个派生类对象并删除它时,如果基类的析构函数不是虚函数,只会调用基类的析构函数,导致派生类特有的资源没有被释放。将其设为虚函数后,动态绑定机制会确保调用到正确的派生类析构函数。
为什么 C++ 默认析构函数不是虚函数?
因为虚函数需要维护虚函数表(vtable)和虚表指针(vptr),这会占用额外的内存空间。对于不会被继承的类来说,开启虚函数机制纯属浪费。因此,只有当类被设计为基类且可能通过基类指针操作子类时,才需要显式声明虚析构函数。
重载和覆盖有什么区别?
- 关系维度:覆盖发生在父子类之间(垂直关系),重载发生在同一个类中(水平关系)。
- 数量限制:覆盖是一对一的关系,重载则是多个方法之间的关系。
- 决定因素:覆盖取决于对象的实际类型(运行时决定),重载取决于参数列表(编译时决定)。
虚函数表如何实现运行时多态?
简单来说,每个包含虚函数的类都有一个虚函数表,存放着虚函数的地址。对象实例中包含一个指向该表的指针。当子类重写父类虚函数时,表中对应位置的地址会被更新。这样,通过基类指针调用虚函数时,程序会根据对象实际的虚表指针找到子类的实现,从而实现多态。
三、系统编程基础
C 语言函数调用的底层原理
大多数 CPU 使用栈来支持函数调用。每次调用都会创建一个栈帧(Stack Frame),用于传递参数、保存返回地址、临时寄存器和局部变量。栈帧由帧指针(如 ebp)和栈指针(如 esp)界定。帧指针指向栈帧头部,栈指针指向栈顶。这种机制保证了函数调用的嵌套和上下文切换能够有序进行。


