【嵌入式C语言题库】C 语言指针进阶选择题(一级指针 · 工程实战篇)003
本文精选10道指针进阶选择题,涵盖真实工程场景中的关键知识点。题目涉及指针运算、数组与指针关系、const/volatile修饰、结构体内存布局、大小端存储等核心概念。每道题配有详细解析,重点剖析常见陷阱,如指针类型转换、内存越界访问、只读存储区修改等问题。最后附上数组与指针的易错点对照表,帮助开发者深入理解指针本质。通过具体案例演示指针在内存操作、寄存器访问和函数参数传递中的实际应用,适合嵌入式
本文整理了 10 道进阶指针选择题,全部源于真实工程应用场景。每题后面都有详细解析,帮助你彻底掌握指针的细节。最后附上一个 数组与指针的易错总结表。
1. 指针差值
uint8_t buf[10];
uint8_t *p = &buf[2];
uint8_t *q = &buf[7];
printf("%td\n", q - p);
输出是多少?
A. 5
B. 7
C. 10
D. 未定义
答案:A. 5
解析:
-
q - p的结果是“两个指针之间相差多少个元素”。 -
p = &buf[2],q = &buf[7],相差 5 个uint8_t元素。 -
因此输出
5。 -
注意:差值不是字节数,而是以元素大小为单位。
2. sizeof 与数组指针
uint8_t arr[5] = {1,2,3,4,5};
uint8_t *p = arr;
printf("%zu %zu %zu\n", sizeof(arr), sizeof(p), sizeof(&arr));
在 32 位 MCU 上输出为:
A. 5 5 5
B. 5 4 4
C. 5 4 20
D. 5 4 8
答案:B. 5 4 4 ✅
解析:
-
sizeof(arr):数组大小 =5 * 1 = 5。 -
sizeof(p):指针大小,在 32 位系统上是 4。 -
sizeof(&arr):类型是uint8_t (*)[5],是“指向整个数组的指针”,本质仍然是一个指针,所以大小是 4。
👉 如果写 sizeof(*(&arr)),那就等价于 sizeof(arr),结果是 5。
这是面试里非常容易出错的细节。
3. 指针运算与越界
uint8_t buf[4] = {10,20,30,40};
uint8_t *p = buf + 4;
printf("%d\n", *(p-1));
输出为:
A. 30
B. 40
C. 越界错误
D. 未定义行为
答案:B. 40
解析:
-
buf + 4指向数组末尾的“下一个位置”(C 标准允许这样)。 -
*(p-1)等价于buf[3] = 40。 -
所以输出
40。 -
注意:如果直接
*p就是越界未定义。
4. const 修饰的陷阱
const uint8_t data[3] = {11,22,33};
const uint8_t *p = data;
uint8_t *q = (uint8_t*)p;
*q = 99;
printf("%d\n", data[0]);
输出结果是:
A. 11
B. 99
C. 未定义行为
D. 编译错误
答案:C. 未定义行为
解析:
-
data是只读存储区(通常放在 Flash 或只读内存)。 -
强转
(uint8_t*)p并写入是非法的。 -
有的编译器/硬件可能让你看到
99,有的可能直接崩溃(HardFault)。 -
C 标准明确规定:修改
const对象是 未定义行为。
5. 结构体与指针偏移
typedef struct {
uint16_t id;
uint8_t status;
uint8_t len;
} Msg;
Msg m = {0x1234, 0xAA, 5};
uint8_t *p = (uint8_t*)&m;
printf("%02X\n", *(p+2));
在 小端 32 位 MCU 上输出为:
A. 12
B. 34
C. AA
D. 05
答案:C. AA
解析:
-
小端:
id = 0x1234在内存里是34 12。 -
结构体内存排布:
-
p[0] = 0x34 -
p[1] = 0x12 -
p[2] = 0xAA -
p[3] = 0x05
-
-
所以
*(p+2) = 0xAA。
6. 指针与 memcpy
uint8_t src[4] = {1,2,3,4};
uint8_t dst[4];
uint8_t *p = src + 1;
memcpy(dst, p, 2);
printf("%d %d\n", dst[0], dst[1]);
输出为:
A. 1 2
B. 2 3
C. 3 4
D. 2 4
答案:B. 2 3
解析:
-
p = src+1→ 指向src[1] = 2。 -
memcpy(dst, p, 2)→ 拷贝2和3到dst[0], dst[1]。 -
输出
2 3。
7. 指针与 volatile
volatile uint8_t reg = 0x55;
uint8_t *p = (uint8_t*)®
*p = 0xAA;
printf("%02X\n", reg);
输出为:
A. 55
B. AA
C. 未定义
D. 编译错误
答案:B. AA
解析:
-
volatile告诉编译器:reg可能随时被硬件修改,不要优化。 -
这里
*p = 0xAA确实修改了该内存位置,reg的值也随之改变。 -
所以输出
0xAA。 -
工程场景:寄存器映射常用
volatile。
8. 指针与函数参数
void func(uint8_t *p) {
*p = 77;
}
int main() {
uint8_t x = 10;
func(&x);
printf("%d\n", x);
}
输出为:
A. 10
B. 77
C. 随机值
D. 编译错误
答案:B. 77
解析:
-
func(&x)把x的地址传进去。 -
在函数里
*p = 77直接修改了main里的x。 -
所以最后输出
77。 -
工程里这就是 指针作输出参数 的经典用法。
9. 字符串与指针
char *s = "Hello";
char t[] = "Hello";
printf("%c %c\n", *(s+1), *(t+1));
输出为:
A. H H
B. e e
C. e H
D. 编译错误
答案:B. e e
解析:
-
s = "Hello"→s指向字符串常量(只读区)。 -
t[] = "Hello"→t是数组,存在栈上。 -
*(s+1)和*(t+1)都是'e'。 -
所以输出
e e。 -
注意:
s[0]可读不可写,而t[0]可读可写。
10. 指针类型转换与对齐
uint32_t val = 0x11223344;
uint8_t *p = (uint8_t*)&val;
printf("%02X\n", *(p+1));
在 小端存储 的 MCU 上输出为:
A. 11
B. 22
C. 33
D. 44
答案:C. 33
解析:
-
小端存储:低字节在低地址。
-
内存排布:
44 33 22 11。 -
p[0] = 0x44 -
p[1] = 0x33 -
p[2] = 0x22 -
p[3] = 0x11 -
所以
*(p+1) = 0x33。
🔑 易错点总结表
| 表达式 | 类型 | 结果 (32 位) | 说明 |
|---|---|---|---|
sizeof(arr) |
数组大小 | 数组总字节数 | 不会退化成指针 |
sizeof(p) |
指针 (uint8_t*) |
4 | 指针大小 |
sizeof(&arr) |
指向数组的指针 | 4 | 本质还是指针 |
sizeof(*(&arr)) |
数组本身 | 数组总字节数 | 常被误解 |
✅ 总结
这一组题涵盖了:
-
数组 vs 指针 的区别(题 2)
-
内存布局和大小端(题 5、10)
-
const 与 volatile 限定(题 4、7)
-
函数指针参数的作用(题 8)
-
常见指针运算和陷阱(题 1、3、6)
建议在做题时多画内存示意图,帮助理解指针和数组之间的关系。
更多推荐
所有评论(0)