指针转换是C语言中的常见操作,允许使用通用接口,例如通过void*操作内存或实现通用数据结构。然而,指针类型的转换伴随着某些风险并遵循严格的标准。
void***在C中存储地址,但不知其指向的内容类型。任何其他指针(例如int*、char*、struct mytype*)可以显式(或隐式)转换为void*并反向转换,而不丢失信息(前提是不发生“缩窄”)。void*),则必须确保该地址上确实存在兼容类型的值。void process(void *data) { int *arr = (int*)data; // 将arr用作int数组 } int main() { double x = 10; process(&x); // 危险:将double*转换为int*, UB }
“任何指针都能安全地转换为void*并反向转换而不丢失信息吗?”
许多人回答“是”——因为C标准保证任何对象指针可以转换为void*并且反向转换而不丢失。但是要记住:如果将非对象指针(例如函数指针)转换为void*或混合不同架构(函数和数据的指针大小不同),将会导致未定义行为。
void foo() {} void *p = (void*)foo; // UB!函数指针不能这样转换
故事
在一个跨平台的数据处理子系统项目中,使用了一个处理程序,将结构指针转换为
void*,再转回原始类型。在迁移到一个int*和double*具有不同对齐方式的架构时,尝试将void*转换为错误类型导致了“总线错误”(程序崩溃)。
故事
在一个嵌入式项目中,程序员实现了一个使用
void*的环形缓冲区,但忘记了严格的对齐要求(为char数组分配内存,作为int*传递)。在某些平台上,数据变得“对硬件不可理解”——导致读取错误和不稳定性。
故事
在动态集合中使用了作为
void*存储地址,但忘记了函数指针不能转换为void*。尝试通过这种字段存储和传递事件处理程序(callback)导致了只有在部分平台上崩溃,而捕捉该错误极其困难。