嵌入式面试八股文·C语言高频面经(一)
目录
- 1. 全局变量和局部变量的区别
-
- 1.1 全局变量
- 1.2 局部变量
- 2. 内存布局
- 3. static关键字
- 4. const关键字
- 5. const 和 #define的区别
- 6. extern关键字
- 7. #include<> 和 #include""的区别
-
- 7.1 使用 #include<>
- 7.2 使用 #include""
- 8. 头文件#ifndef/#define/#endif的作用
- 9. volatile声明的作用
- 10. strcpy与memcpy的区别
-
- 10.1 strcpy
- 10.2 memcpy
- 11. 数组名与指针的区别
-
- 11.1 数组名
- 11.2 指针
- 12. 结构体和共用体的区别
-
- 12.1 结构体
- 12.2 共用体
- 13. 数组指针与指针数组的区别
-
- 13.1 数组指针(pointer to an array)
- 13.2 指针数组(array of pointers)
- 14. 指针函数与函数指针的区别
-
- 14.1 指针函数(function returning a pointer)
- 14.2 函数指针(pointer to a function)
- 15. 原码、反码、补码的定义
- 16. malloc和calloc的区别
-
- 16.1 malloc
- 16.2 calloc
- 17. 在1G内存的计算机中能否malloc(1.2G)?为什么?
- 18. 简述内存泄漏,如何检测和避免?
-
- 18.1 常见内存泄露情况
- 18.2 检测内存泄漏
- 18.3 避免内存泄漏:
- 19. (内存)堆和栈的区别
-
- 19.1 堆栈空间分配不同
- 19.2 堆栈缓存方式不同
- 19.3 生长方向
- 19.4 生命周期:
- 19.5 空间大小
- 19.6 能否产生碎片
- 20. 在函数中申请堆内存需要注意什么
- 21. 请你说说内存碎片
- 22. 什么是内存池
- 23. 为什么需要内存池?
- 24. 初始化为0的全局变量在bss还是data
- 25. 使用指针需要注意什么?
1. 全局变量和局部变量的区别
1.1 全局变量
① 在函数外部声明的变量,整个程序都可以访问;
② 声明时会被默认初始化,可以在任何函数中使用;
③ 生命周期长,整个程序执行期间都存在;
④ 全局变量存储在全局数据区(data)中。
1.2 局部变量
① 在函数内部或代码块内部声明的变量,只能在所属的函数或代码块中访问;
② 声明时没有默认初始化,需要手动赋值才能使用;
③ 生命周期短,只在所属的函数或代码块的执行期间存在;
④ 局部变量存储在栈区(stack)。
2. 内存布局
① 代码段(Text Segment):存储程序的机器码,通常是只读的,因为程序的指令在执行过程中不应该被修改。
② 数据段(Data Segment):存储已初始化的全局变量和静态变量,这些变量在程序开始运行时已经赋予了初始值。
③ BSS段(Block Started by Symbol):存储未初始化的全局变量和静态变量,它们在程序开始运行时会自动初始化为0或者空指针。
④ 堆(Heap):用于动态内存分配,用于存放程序运行时动态申请的内存。(程序员可以通过函数(如malloc、calloc等)或者操作系统提供的接口来申请和释放堆内存,堆从低地址向高地址增长。)
⑤ 栈(Stack):存放函数的局部变量、函数参数值以及函数调用和返回时的相关信息。栈区是按照“先进后出”的原则进行管理,内存的分配和释放是自动进行的,栈从高地址向低地址增长。是一块连续的空间。
⑥ 共享区:也称为文件映射或共享内存,用于实现不同进程之间的内存共享。
3. static关键字
① 声明静态变量,使其生命周期延长或作用域限定在当前文件内;
② 声明静态函数,使其作用域限定在当前文件内;
③ 声明静态成员变量,使其属于类本身而不是对象,多个对象共享同一份内存;
④ 使用静态限定符,控制变量的初始化和生命周期。
4. const关键字
① 值不可修改:一旦常量被赋值后,其值将保持不变,不能再对其进行修改;
② 作用域限制:常量的作用域通常被限制在声明时所在的作用域内部;
③ 编译时确定:常量的值在编译时就已确定,并在运行时保持不变。
5. const 和 #define的区别
① const是一种编译器关键字,而#define是预处理器指令。const在编译阶段进行处理,而#define在预处理阶段进行处理;
② const定义的常量具有类型,而#define没有。const在声明时需要指定常量的类型,编译器会进行类型检查。而#define只是简单的文本替换,没有类型检查;
③ const定义的常量有作用域限制,可以根据声明位置的不同而有不同的作用域。而#define定义的常量没有作用域限制,整个程序中都有效;
④ const生成符号表中的一个符号,有明确的名字和类型,可以进行调试和符号查找。而#define没有生成符号表,不会产生对应的符号。
6. extern关键字
① 声明一个在其他文件中定义的外部变量或函数;
② 告诉编译器在链接过程中需要找到对应的定义;
③ 允许在当前文件中使用这些外部变量或函数而不需要重新定义。
7. #include<> 和 #include""的区别
7.1 使用 #include<>
① 用于包含系统提供的标准库头文件;
② 在编译器的搜索路径中寻找头文件;
③ 编译器会先在系统的标准头文件目录中查找,如果找不到则报错。
7.2 使用 #include""
① 用于包含用户自定义的头文件或项目中使用的其他非系统头文件;
② 在当前源文件的相对路径或指定的绝对路径中寻找头文件;
③ 编译器会首先在当前源文件所在目录中查找,如果找不到再根据指定的路径查找。
8. 头文件#ifndef/#define/#endif的作用
① #ifndef:用于判断当前头文件是否已经被包含。如果该宏之前没有被定义过,则继续编译下面的代码。如果该宏之前已被定义过,则跳过下面的代码,直接到 #endif;
② #define:用于定义一个宏。通过定义一个特定的宏名称,例如MY_HEADER_H表示头文件已被包含;
③ #endif:用于结束 #ifndef / #define / #endif 块。标记了头文件的结束位置。
#ifndef MYHEADER_H // 如果 MYHEADER_H 还没有被定义
#define MYHEADER_H // 定义 MYHEADER_H
void sayHello(); // 函数声明
const int MAX_VALUE = 100; // 常量定义
#endif // 结束条件编译
9. volatile声明的作用
volatile声明的变量是指可能会被意想不到地改变的变量,这样编译器就不会轻易优化该变量。它主要用于多线程编程中,用来保证共享变量的内存可见性。(注:指针也可用volatile)
三个常见场景:
① 多线程中的共享变量
② 中断程序中访问到的非自动变量
③ 并行设备的硬件寄存器
10. strcpy与memcpy的区别
10.1 strcpy
① 用于字符串拷贝。
② 源字符串中的内容会被复制到目标字符串中,直到遇到字符串结束符 ‘\0’。
③ 目标字符串必须有足够的空间来存储被复制的内容,否则可能导致缓冲区溢出。
10.2 memcpy
① 用于字节级别的内存拷贝。
② 可以拷贝任意类型的内存块,不仅限于字符串。
③ 不会检查字符串结束符,通过指定要拷贝的字节数进行拷贝。
④ 可以用于拷贝部分或完整的数组、结构体等。
11. 数组名与指针的区别
11.1 数组名
① 是一个常量指针,指向数组的首元素。
② 大小固定为整个数组的大小。
③ 无法被改变或重新赋值。
④ 无法进行指针运算。
11.2 指针
① 是一个变量,存储一个内存地址。
② 大小固定为指针类型的大小。
③ 可以指向任意类型的对象。
④ 可以被改变或重新赋值。
⑤ 可以进行指针运算,如加法、减法等。
12. 结构体和共用体的区别
12.1 结构体
① 成员在内存中独立存储,每个成员占用独立的内存空间。
② 内存占用是成员之和,每个成员都占用独立的空间。
③ 成员可以同时被访问,通过成员名字来访问。
④ 适合存储和处理多个不同类型的数据,如员工信息、图形对象等。
12.2 共用体
① 成员共享同一块内存空间,只能存储一个成员的值。
② 内存占用是最大成员的大小,