嵌入式八股文之 struct 和 union 的区别、大厂真题1、头文件中的#ifdef/#define/#endif作用是什么?
一、struct (结构体) 和 union (联合体) 的区别
(1) 在存储多个成员信息时,编译器会自动给 struct 每个成员分配存储空间,strcut 可以存储多个成员信息,而 Union 每个成员会用同一个存储空间,大小为最大成员的大小。
(2) 都是由多个不同的数据类型成员组成,但在任何同一时刻,Union 只存放了一个被先选中的成员,而结构体的所有成员都存在。
(3) 对于 Union 的不同成员赋值,将会对其他成员重写,原来成员的值就不存在了,而对于 struct 的不同成员赋值是互不影响的。
二、大厂真题
请写出 printf 的值(基于小端序)
#include <stdio.h>int main() {union {int i;struct {char first;char second;} half;} number;number.i = 0x4241;printf("%c%c\n", number.half.first, number.half.second); // 输出:ABnumber.half.first = 'a';number.half.second = 'b';printf("%x\n", number.i); // 输出:6261(十六进制)return 0;
}
解:本道题设计了计算机组成原理、操作系统、嵌入式系统开发、网络编程
1. 十六进制转换为十进制
(1) 0x 是一个前缀标识符,用于明确表示后面的数字是十六进制表示法。
(2) 记忆转换表:
十六进制 | 十进制 |
0x0 | 0 |
0x1 | 1 |
0x2 | 2 |
0xA | 10(A 代表了10) |
0xB | 11(B 代表了11) |
0xC | 12 |
0xD | 13 |
0xE | 14 |
0xF | 15 |
0x10 | 16(正好是十六进制) |
0xFF | 255 |
(3) 0x4241 的十进制换算:
0x4241 = 4 × 16³ + 2 × 16² + 4 × 16¹ + 1 × 16⁰
2. ASCII 表
字符 | ASCII 十进制 |
A | 65 |
B | 66 |
C | 67 |
a | 97 |
b | 98 |
c | 99 |
3. 代码说明:
(1) 联合体定义:
union {int i; // 4字节整型(平台相关,通常4字节)struct {char first; // 1字节字符char second; // 1字节字符} half; // 共占2字节
} number;
- 联合体 number 包含两个成员:整型 i(4字节)和结构体 half(2字节)
- 小端序:低位数据存储在低地址
(2) 写入整型 i 并读取字符
number.i = 0x4241; // 十六进制赋值
printf("%c%c\n", number.half.first, number.half.second); // 输出:AB
① 内存布局:
内存地址 | 低地址 → 高地址 |
值(十六进制) | 0x41 |
对应成员 | first |
② 解析:
- 0x41 是字符 'A' 的 ASCII 码,0x42 是 'B'
- 因此 first 读取到 0x41 ('A'),second 读取到 0x42('B'),输出 AB
(3) 写入字符并读取整型 i
number.half.first = 'a'; // ASCII 0x61
number.half.second = 'b'; // ASCII 0x62
printf("%x\n", number.i); // 输出:6261(十六进制)
① 整型 i 的值(小端序):
- 内存低位到高位 0x61,0x62,0x00,0x00 → 拼合为 0x000006261
② 数据拼合过程:(计算机以 字节 为基本存储单位,每个字可表示的数值范围是 0-255(共 256 种可能)
内存地址: 低 ────────────────▶ 高
字节值: 0x61 0x62 0x00 0x00
权重: 256^0 256^1 256^2 256^3
计算整数值:
(0x61 × 256⁰) + (0x62 × 256¹) + (0x00 × 256²) + (0x00 × 256³)
= (97 × 1) + (98 × 256) + (0 × 65536) + (0 × 16777216)
= 97 + 25088
= 25185
转换为十六进制
25185 (十进制) = 6261 (十六进制)
- 反复除以 16:用十进制数连续除以 16,记录每次的余数
- 余数转16 进制:余数 0-9 保持原数字,10-15 转为字母 A-F
- 逆序排列:最后得到的余数数列反向排列
- 添加前缀:结果前加 0x
(4) 答案:
AB
6261
三、头文件中的 #ifdef/#define/#endif 作用是什么?
1. 核心目的是防止头文件重复包含:避免同一个头文件被多次 #include 时出现重复声明/定义错误
2. 条件编译控制:根据宏是否定义,决定编译哪些代码
3. 跨平台支持:为不同平台编译不同代码