error的作用是什么?
error 指令用于让预处理器发出一条错误信息,并会中断编译过程。例如,在Linux内核驱动代码中,常使用此指令进行配置检查。

这段示例代码的作用是:当RX_BUF_IDX宏的值不在0~3范围内时,预处理器阶段就会通过#error指令输出错误提示“Invalid configuration for 8139_RXBUF_IDX”,从而阻止编译继续进行。
编译结果如下:

位操作的基本使用
如何用宏实现对一个32位数据的特定位进行置1操作?
#define SET_BIT(x, bit) (x |= (1 << bit)) /* 将第bit位置1 */


隐式转换规则
以下代码的输出结果是什么?原因何在?
#include <stdio.h>
int main(void) {
unsigned int a = 6;
int b = -20;
if (a + b > 6)
printf("a+b大于6\n");
else
printf("a+b小于6\n");
return 0;
}
程序输出结果为:
a+b大于6
其原理在于,C语言在进行算术运算时,编译器会将有符号数b隐式转换为无符号数。因此,a + b等价于a + (unsigned int)b。
在32位环境下,b转换后的值为0xFFFFFFFF - 20 + 1 = 4294967276,因此a + b的结果远大于6。C语言遵循 “正常算术转换” 规则,转换顺序为:
double > float > unsigned long > long > unsigned int > int
即当不同类型操作数进行运算时,排名靠后的类型会隐式转换为排名靠前的类型。想深入了解网络协议中的底层交互,可以学习 网络/系统 相关知识。
typedef与#define的区别
- 语法:
#define之后不加分号,而typedef之后需要加分号。
-
类型扩展:#define定义的宏类型名可以用其他类型说明符进行扩展,而typedef定义的类型别名则不行。
#define INT1 int
unsigned INT1 n; // 正确
typedef int INT2;
unsigned INT2 n; // 错误
-
连续定义:使用typedef能保证定义的所有变量均为同一类型,而#define无法保证。
#define P_INT1 int*
P_INT1 p1, p2; // 展开为 int *p1, p2; 即p1是指针,p2是int变量
typedef int* P_INT2;
P_INT2 p1, p2; // p1和p2都是指向int的指针
写一个MAX宏
一个基础的MAX宏实现如下:
#define MAX(x, y) ((x) > (y) ? (x) : (y))
使用括号包裹参数可以避免因运算符优先级导致的问题。这个宏已能满足日常使用,但存在边界情况(如副作用),更严谨的写法可参考进阶资料。
死循环
在嵌入式系统中,无限循环(死循环)常用于主程序或任务调度。常见的C语言实现方式有三种:
- while循环
while(1) {
// 循环体
}
- for循环
for(;;) {
// 循环体
}
- goto语句
Loop:
// 循环体
goto Loop;
static的作用
在C语言中,关键字static主要有三种作用:
- 在函数体内修饰局部变量:使该变量的生命周期延长至整个程序运行期,但作用域不变,每次函数调用时其值保持上一次的结果。
- 在模块内(函数体外)修饰全局变量:将变量的作用域限制在定义它的源文件内,使其成为该文件内部的“全局”变量,避免命名冲突。
- 在模块内修饰函数:将函数的作用域限制在定义它的源文件内,使其成为该文件内部的私有函数。
const的作用
理解以下声明的含义是掌握C语言核心的关键:
const int a;
int const a; // 前两者等价,a是一个值不可变的整型常量
const int *a; // a是一个指针,指向一个整型常量(指针可变,指向的值不可变)
int * const a; // a是一个常量指针,指向一个整型变量(指针不可变,指向的值可变)
int const * a const;// a是一个常量指针,指向一个整型常量(指针和指向的值都不可变)
深入理解指针和内存管理是嵌入式开发的基石,更多关于 C/C++ 的核心概念可以系统学习。
volatile的作用
volatile关键字用于告诉编译器,该变量的值可能会被程序以外的因素(如硬件、中断、其他线程)意外改变,因此编译器不应对其做优化假设,每次使用都必须直接从内存中读取。
典型应用场景:
- 访问内存映射硬件寄存器。
- 在中断服务程序中访问的非自动变量。
- 多线程应用中由多个任务共享的变量。
进阶问题:
- 一个变量可以同时是
const和volatile吗? 可以。例如一个只读硬件状态寄存器,它是volatile的因为其值可能硬件改变,程序又不应修改它,所以也是const的。
- 指针可以是
volatile的吗? 可以。例如一个被中断服务程序修改的、指向缓冲区的指针。
- 以下函数有何错误?
int square(volatile int *ptr) {
return *ptr * *ptr;
}
错误:由于ptr指向volatile变量,其值可能在两次解引用之间被改变,导致返回的不是期望的平方值。
正确写法:
long square(volatile int *ptr) {
int a = *ptr;
return a * a;
}
在多线程或涉及底层硬件操作的场景中,正确使用volatile至关重要,这常常是嵌入式系统与通用程序开发的区别之一。
变量定义
用变量a给出以下定义:
a) 一个整型数
b) 一个指向整型数的指针
c) 一个指向指针的指针,它指向的指针是指向一个整型数
d) 一个有10个整型数的数组
e) 一个有10个指针的数组,该指针是指向一个整型数
f) 一个指向有10个整型数数组的指针
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数
h) 一个有10个函数指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数
答案:
a) int a;
b) int *a;
c) int **a;
d) int a[10];
e) int *a[10];
f) int (*a)[10];
g) int (*a)(int);
h) int (*a[10])(int);
中断函数
在嵌入式开发中,中断服务程序(ISR)有严格的编写规范。以下是一个使用__interrupt关键字定义的ISR示例,请指出其问题:
__interrupt double compute_area(double radius) {
double area = PI * radius * radius;
printf(" Area = %f", area);
return area;
}
问题分析:
- 返回值:ISR通常不应有返回值。该函数声明为返回
double,不符合惯例。
- 参数传递:ISR通常不接收参数。参数传递机制在中断上下文中未定义或不安全。
- 浮点运算与不可重入:许多处理器架构中,浮点运算是不可重入的,且在ISR中进行浮点操作可能导致寄存器保存/恢复开销巨大,或直接不被支持。
- 调用非可重入函数:
printf()函数通常是非可重入的,并且非常耗时,在要求快速响应的ISR中使用它是极其不恰当的。ISR应尽量简短高效,仅做最必要的标志设置或数据移动。
编写高效可靠的中断服务程序是嵌入式系统的核心技能,这需要对 Linux 等操作系统的中断机制有深入理解。