2. 指针类型与强制转换
任务目标
编写一段C代码,演示不同类型指针之间的强制转换规则,并通过内存访问结果验证:指针类型转换只改变解释内存的方式,不改变内存地址本身;错误的对齐或长度假设可能导致非预期读取。
核心实现
#include <stdio.h>
#include <stdint.h>
int main(void) {
// 原始数据:4字节,十六进制 0x44332211
uint32_t value = 0x44332211;
uint32_t *p_u32 = &value;
// 1. 相同宽度类型转换(uint32_t* → int32_t*)
int32_t *p_i32 = (int32_t *)p_u32;
printf("地址: %p\n", (void *)p_u32);
printf("uint32_t 视角: 0x%08X\n", *p_u32);
printf("int32_t 视角: %d (0x%08X)\n", *p_i32, (uint32_t)*p_i32);
// 2. 窄化转换(uint32_t* → uint8_t*),观察字节序
uint8_t *p_bytes = (uint8_t *)p_u32;
printf("\n字节访问 (小端序):\n");
for (int i = 0; i < 4; i++) {
printf(" byte[%d] = 0x%02X\n", i, p_bytes[i]);
}
// 3. 跨宽度转换(uint32_t* → uint64_t*),演示越界读取风险
uint64_t *p_u64 = (uint64_t *)p_u32;
printf("\nuint64_t 视角: 0x%016llX (可能越界读取)\n",
(unsigned long long)*p_u64);
// 4. 指针与整数互转(仅演示,不推荐)
uintptr_t addr = (uintptr_t)p_u32;
uint32_t *p_back = (uint32_t *)addr;
printf("\n整数往返转换后值: 0x%08X\n", *p_back);
return 0;
}
编译与运行
gcc -o ptr_cast ptr_cast.c -Wall -Wextra
./ptr_cast
验证方法与判定标准
- 地址一致性:所有指针打印的地址值相同,证明类型转换不改变地址。
- 同宽转换:
int32_t视角打印的数值与uint32_t视角的十六进制位模式一致(如0x44332211对应十进制1144201745),说明仅解释方式改变。 - 字节序观察:在小端机器上,
byte[0]=0x11、byte[3]=0x44;在大端机器上顺序相反。此结果直接反映硬件字节序。 - 越界风险:
uint64_t读取会访问value之后的4字节内存,输出值取决于相邻内存内容,不可预测——这正是跨宽度转换的危险所在。 - 整数往返:
uintptr_t保证指针→整数→指针的往返转换后值不变,输出仍为0x44332211。
关键说明
- 转换规则本质:C语言中指针类型转换仅改变编译期类型信息,运行时按新类型解释原地址处的字节序列。
- 对齐要求:若目标类型对齐要求更严格(如
uint32_t*转uint64_t*),可能引发未定义行为;本示例中value的对齐通常满足要求,但越界读取仍属未定义行为。 - 实际工程中,
uint8_t*常用于内存拷贝、协议解析;uintptr_t仅用于特殊场景(如嵌入式寄存器地址映射),日常编程应避免指针与整数互转。