1. 嵌入式系统中经常要用到无限循环,如何用C编写死循环?
答:while(1){} 或者 for(;;){}。
2. 程序的局部变量存在于哪里,全局变量存在于哪里,动态申请数据存在于哪里?
答:程序的局部变量存在于栈区;全局变量存在于静态区;动态申请数据存在于堆区。
3. 关键字const有什么含义?
答:
1) 表示只读。
2) 使用关键字const有助于编译器产生更紧凑的代码。
3) 使编译器能自然地保护那些不希望被改变的参数,防止其被无意的代码修改。
4. 请问以下代码有什么问题?

答:代码没有为str指针分配有效的内存空间,strcpy操作将导致越界内存读写。虽然在某些环境下可能“正确”输出“hello”,但程序最终会因非法内存访问而崩溃。
5. 已知一个数组table,用一个宏定义,求出数据的元素个数
答:#define NTBL (sizeof(table)/sizeof(table[0]))
6. 写一个“标准”宏MIN ,这个宏输入两个参数并返回较小的一个。
答:#define MIN(A,B) ((A) <= (B) ? (A) : (B))
考点:
- 掌握
#define在宏定义中的应用。在C99标准引入内联函数之前,宏是生成内联代码的主要方式,对于追求性能的嵌入式系统至关重要。
- 理解三元条件运算符
? :。编译器通常能为它生成比等价的if-then-else结构更优的代码。
- 懂得在宏定义中小心地用括号将参数括起来,以避免因运算符优先级导致的意外错误。
7. do……while和while有什么区别?
答:do...while循环先执行一次循环体,再进行条件判断;而while循环是先进行条件判断,条件为真才执行循环体。
8. 什么是预编译,何时需要预编译?
答:预编译指令指示编译器在程序正式编译前执行某些操作。它通常用于以下场景:
- 处理不经常改动的大型代码体。
- 当程序由多个模块组成,且所有模块都使用一组相同的头文件和编译选项时,可以将这些头文件预编译为一个“预编译头文件(.pch)”,以提升编译速度。预编译指令可以放在程序的任何位置。
9. 一个32位的机器,该机器的指针是多少位?
答:指针的位数取决于CPU的地址总线位数。对于32位机器,其地址总线通常为32位,因此指针的长度是4个字节(32位)。
10. 局部变量能否和全局变量重名?
答:可以。当局部变量与全局变量同名时,在局部变量的作用域内(通常是函数内),引用该名称会访问局部变量,全局变量被暂时“屏蔽”。
11. 引用与指针有什么区别?
答:
1) 初始化:引用在定义时必须初始化,指针可以不初始化。
2) 可变性:引用一旦初始化后就不能再指向其他对象,而指针可以改变其指向。
3) 空值:不存在指向空值(NULL)的引用,但指针可以指向NULL。
4) 操作:对引用的所有操作都等同于对其所绑定对象的直接操作;指针则需要使用解引用操作符*。
12. 关键字static在C语言中的作用是什么?
答:static关键字主要有三种作用:
- 在函数内部:声明一个静态局部变量。该变量在函数调用结束后不会销毁,其值在程序的整个生命周期内保持不变,但作用域仍仅限于该函数内。
- 在模块内(函数外部):声明一个静态全局变量。该变量只能被定义它的源文件内的所有函数访问,对其他源文件不可见,实现了访问限制。
- 在模块内:声明一个静态函数。该函数只能被定义它的源文件内的其他函数调用,对其他源文件不可见。
13. static全局变量与普通的全局变量有什么区别?static函数与普通函数有什么区别?
答:
- static全局变量 vs 普通全局变量:两者存储方式相同(静态存储区)。核心区别在于作用域。普通全局变量的作用域是整个程序(所有源文件),而
static全局变量的作用域仅限于定义它的源文件内,这有助于避免命名冲突和实现信息隐藏。
- static函数 vs 普通函数:区别同样在于作用域。普通(非静态)函数默认具有外部链接性,可以被其他源文件调用(需要声明)。而
static函数具有内部链接性,只能在其定义的源文件内被调用。
14. 进程之间通信的途径有哪些?
答:进程间通信(IPC)的主要方式包括:
- 无名管道 (pipe):半双工,数据单向流动,只能用于具有亲缘关系(如父子进程)的进程间。
- 有名管道 (named pipe / FIFO):半双工,但提供了一个路径名与之关联,允许无亲缘关系的进程间通信。
- 消息队列 (message queue):消息的链表,存放于内核中,克服了管道只能承载无格式字节流和缓冲区大小受限等缺点。
- 共享内存 (shared memory):映射一段能被多个进程访问的内存。这是最快的IPC方式,通常需要配合信号量等机制来同步。
- 信号量 (semaphore):一个计数器,主要用于控制多个进程对共享资源的访问,实现进程间的同步与互斥。
- 信号 (signal):一种异步通信机制,用于通知接收进程某个事件已经发生。
- 套接字 (socket):功能最强大的IPC机制,可用于不同主机间的进程通信。
15. 产生死锁的原因是什么?
答:死锁是指多个并发进程因竞争系统资源而陷入相互等待的状态,导致都无法继续推进。其本质原因有两个:
- 系统资源有限。
- 进程推进的顺序不合理。
16. 死锁的4个必要条件
答:以下四个条件必须同时满足,才会发生死锁:
- 互斥:资源一次只能被一个进程使用。
- 占有且等待:进程在等待新资源的同时,继续占有已分配的资源。
- 不可抢占:进程已获得的资源在未使用完之前,不能被强行剥夺。
- 循环等待:存在一个进程-资源的环形等待链。
17. 死锁的处理方式有哪些?
答:死锁的处理策略主要分为四类:
- 预防死锁:通过破坏死锁的四个必要条件之一来预防。
- 破坏“占有且等待”:要求进程一次性申请所有所需资源。
- 破坏“不可抢占”:允许系统剥夺进程已占有的资源。
- 破坏“循环等待”:为所有资源类型规定一个线性顺序,要求进程按序申请资源。
- 避免死锁:在资源动态分配过程中,使用算法(如银行家算法)预测此次分配是否会导致系统进入不安全状态,从而决定是否分配。
- 检测死锁:允许系统进入死锁状态,但通过定期检测资源分配图等方法来发现死锁。
- 解除死锁:检测到死锁后,通过剥夺资源或终止进程(选择一个“代价最小”的进程终止)来恢复系统。
18. 进程和线程有什么区别?
答:进程是资源分配和独立运行的基本单位,线程是CPU调度和执行的基本单位,是进程内的一个更小实体。主要区别如下:
- 资源开销:进程拥有独立的地址空间,创建、切换开销大;线程共享进程资源,创建、切换开销小。
- 通信方式:线程间可直接读写同一进程的全局变量、堆内存进行通信,简单但需注意同步;进程间通信(IPC)必须通过操作系统提供的机制(管道、共享内存等),相对复杂。
- 健壮性:一个线程崩溃会导致整个进程崩溃;进程之间则相互隔离,一个进程崩溃通常不影响其他进程。
- 执行与调度:线程是程序执行的实际单位,也是CPU调度的基本单位;进程不能直接被调度。
如何选择:
- 对资源保护和管理要求高、可接受较大开销时,使用多进程。
- 要求高效率、频繁切换、且资源共享和通信频繁时,使用多线程。
19. 线程是否具有相同的堆栈?
答:每个线程都有自己独立的堆栈,用于保存局部变量、函数调用现场等信息。这是线程能独立调度的基础。
20. TCP与UDP有啥区别?
答:TCP(传输控制协议)和UDP(用户数据报协议)是传输层的两大核心协议,主要区别如下:
| 特性 |
TCP |
UDP |
| 连接 |
面向连接,需三次握手建立连接 |
无连接,直接发送数据 |
| 可靠性 |
可靠:通过确认应答、重传、排序、流量控制等机制确保数据正确、有序、不丢失 |
不可靠:尽最大努力交付,不保证数据到达 |
| 数据传输 |
基于字节流的可靠传输 |
基于数据报的不可靠传输 |
| 速度与开销 |
速度相对较慢,系统开销大(需要维护连接状态) |
速度快,系统开销小 |
| 通信模式 |
仅支持一对一通信 |
支持一对一、一对多、多对多通信 |
| 适用场景 |
文件传输、电子邮件、Web浏览等要求可靠性的场景 |
音视频流媒体、实时游戏、DNS查询等对速度敏感、能容忍少量丢包的场景 |
在当今网络质量普遍提升的背景下,UDP凭借其低延迟、高效率的特点,在实时性要求高的领域(如在线游戏、视频会议)应用越来越广泛。应用层可以在UDP之上实现自定义的可靠传输逻辑,以获得比TCP更灵活的体验。
希望这份涵盖了C语言核心与操作系统基础的面试题解析,能帮助你更好地备战嵌入式开发岗位。如果你在学习和实践中遇到更多问题,欢迎到云栈社区与其他开发者交流探讨。
[付费]STM32嵌入式资料包
|