1、关键字static有什么作用?

static在不同场景下有不同含义:

  • 静态局部变量:函数内用static修饰的变量,仅在首次调用时初始化,后续调用保留上次结束时的值,生命周期延长至整个程序运行期。

  • 静态全局变量:全局变量前加static,作用域被限制在定义它的源文件内,其他文件无法通过extern引用。

  • 静态函数:用static修饰的函数,作用域同样限于定义它的源文件,可避免不同文件的函数名冲突。

2、volatile关键字的作用是什么?在什么场景下使用?

volatile告诉编译器该变量可能被意外修改,禁止编译器对其访问进行优化,每次必须从内存中重新读取。

三大使用场景:

  1. 硬件寄存器映射:寄存器值可能被硬件自行改变,如定时器计数寄存器。

  2. 中断服务程序中修改的变量:ISR中修改的全局标志位。

  3. 多任务共享变量:多线程/多任务环境下各任务共享的标志位。

典型代码示例

c

volatile unsigned int *timer_reg = (volatile unsigned int *)0x40000000;
unsigned int value = *timer_reg;  // 每次都从硬件寄存器读取

3、const关键字的含义和用法?

const表示“只读”,防止变量被意外修改。常见用法:

  • const int a; 或 int const a; — a是常整型数,值不可修改。

  • const int *p; — 指针指向的内容不可变,但指针本身可变(常量指针)。

  • int * const p; — 指针本身不可变,但指向的内容可变(指针常量)。

  • const int * const p; — 指针和指向的内容都不可变。

4、局部变量、全局变量、动态申请数据分别存放在哪里?

  • 局部变量存放在 栈区(stack),函数返回后自动释放。

  • 全局变量和静态变量存放在 静态区(data/bss段)。

  • 动态申请的数据(malloc等)存放在 堆区(heap),需手动释放。

5、堆和栈的区别是什么?

比较项 栈(Stack) 堆(Heap)
管理方式 系统自动分配释放 手动分配释放(malloc/free)
空间大小 较小(通常几KB到几十KB) 较大(受系统可用内存限制)
存储内容 局部变量、函数参数、返回地址 动态分配的数据
访问速度
内存分布 连续 可能不连续,易产生碎片

6、什么是栈溢出?如何预防?

栈溢出是指栈中存储的数据超出栈的最大容量,覆盖其他数据(如函数返回地址),导致程序崩溃或异常。

常见导致栈溢出的操作:

  • 在函数内定义过大的局部数组或结构体。

  • 函数递归调用过深。

  • 函数参数过多或传递大型结构体(值传递)。

预防措施

  • 避免定义超大局部变量,改用静态变量或动态内存。

  • 控制递归深度,尽量改用迭代。

  • 传递大型数据时使用指针,减少栈内存占用。

  • 配置合理的栈大小,并使用工具监控栈使用情况。

7、malloc、calloc、realloc的区别?

  • malloc:分配指定字节大小的连续内存,内容未初始化。

  • calloc:分配指定个数和每个元素大小的连续内存,并将内存初始化为0。

  • realloc:调整已分配内存的大小,若原内存后空间不足则重新分配并拷贝数据。

常见问题:内存泄漏(未free)、野指针(free后未置NULL)、重复释放、内存越界。

8、sizeof和strlen的区别?

  • sizeof是编译时运算符,返回变量或类型所占内存的字节数,计算时包含末尾的\0

  • strlen是运行时库函数,返回字符串的长度,不包含末尾的\0

9、结构体字节对齐是什么?为什么需要关注?

数据在内存中的起始地址必须为特定值(通常是数据类型大小的整数倍)的整数倍。

为什么需要关注

  • 现代计算机按字(如4字节或8字节)访问内存,未对齐的数据可能需要多次访问,降低性能。

  • 部分处理器只能访问对齐数据,否则会产生硬件异常。

结构体对齐规则:基本数据类型对齐值通常等于自身大小,结构体对齐值为其成员中最大对齐值,成员间可能存在填充字节。

10、C程序从源代码到可执行文件经历了哪些步骤?

预处理 → 编译 → 汇编 → 链接:

  1. 预处理:处理#开头的指令(宏替换、头文件展开、条件编译、删除注释)。

  2. 编译:将预处理后的代码转换为汇编代码,进行语法语义分析。

  3. 汇编:将汇编代码转换为机器码,生成目标文件(.o/.obj)。

  4. 链接:将多个目标文件和库文件链接成可执行文件,解析符号引用。

11、define和const的区别?

比较项 #define const
处理阶段 预处理阶段替换 编译阶段生效
类型检查
内存分配 不分配内存(占代码段) 分配内存(占数据段)
调试 无法调试(已被替换) 可调试

12、大端(Big-Endian)和小端(Little-Endian)的区别?

  • 大端:数据的高位字节存储在内存的低地址端,低位字节存储在内存的高地址端(网络协议多使用大端)。

  • 小端:数据的低位字节存储在内存的低地址端,高位字节存储在内存的高地址端(ARM/x86默认使用小端)。

检测代码:可用union或指针方式,读取一个多字节整数的最低地址字节来判断。

13、如何用C语言编写死循环?

两种常用方式:

  • while(1) { ... }

  • for(;;) { ... }

14、指针和数组名有什么区别?

  • 数组名是常量,指向数组首元素的地址,不可被赋值修改。

  • 指针是变量,可以指向任意地址,可以被赋值修改。

  • sizeof(数组名)返回整个数组的大小,sizeof(指针)返回指针变量本身的大小(如4字节或8字节)。

15、用宏定义写出求两个数最小值的宏,并说明注意事项

c

#define MIN(a, b) ((a) < (b) ? (a) : (b))

注意事项

  • 参数必须加括号,防止运算符优先级问题。

  • 整体表达式也需加括号,防止在复杂表达式中出错。

  • 宏可能导致副作用(如MIN(a++, b)中a++被计算两次)。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐