使用void*存储通用对象的弊端及相关C技术疑问
关于C语言通用栈(void*实现)的几个疑问解答
1. 哪些数据无法以指针形式表示?
你提到的int*、struct foo*这些都是对应类型的指针,但这里的“无法以指针形式表示”指的是本身没有可获取内存地址的数据,比如:
- 结构体中的位字段(bit-field):它是打包存储的,不属于独立的内存对象,标准不允许对其取地址。
- 寄存器变量:用
register关键字声明的变量(现代编译器虽很少真的放到寄存器,但标准明确禁止对其取地址)。 - 另外还有一种常见误区:如果你想直接存储“值本身”而非指向值的指针(比如存一个
int值),虽然可以用指针指向栈上的int变量,但如果这个变量是局部的,函数返回后内存被回收,栈里的指针就变成野指针,这种情况本质是值的生命周期问题,而非没有对应指针类型。
2. 作者为何以字符串和动态分配结构体为例?
这两个例子是通用栈最安全、最典型的使用场景,核心原因是它们的生命周期完全可控:
- 字符串字面量存储在静态内存区,生命周期贯穿整个程序,push它的指针后不会出现悬空问题;动态分配的字符串(
malloc创建)生命周期由开发者控制,只要不提前free,栈里的指针就一直有效。 - 动态分配的结构体同理,堆上的对象不受函数栈帧销毁影响,push指针后可以安全地在后续操作中转换类型使用。
你说的栈上int对象确实可以取指针push,但如果这个int是局部变量,函数返回后内存被回收,栈里的指针就变成野指针,访问它会触发未定义行为——作者举的例子是在避开新手容易踩的坑,教你正确用法:通用栈适合存储拥有稳定生命周期的堆/静态区对象的指针,而非栈上临时变量的指针。
3. void*与函数指针的转换限制,以及“void语境”的含义?
函数指针的转换问题
C标准明确规定:对象指针(指向数据的指针,比如int*、struct*)可以和void*互相转换,转换后不会丢失信息;但函数指针和void*之间的转换是未定义行为。
原因是函数指针和对象指针的底层存储可能存在差异:部分架构中,函数指针指向代码段,对象指针指向数据段,两者的长度、编码格式可能不同,强行转换会导致指针值损坏,无法正确调用函数。
“void语境”的含义
书中的表述有点模糊,准确来说:void*本身不能直接解引用或进行指针算术运算——因为它没有类型信息,编译器不知道它指向的对象的大小、内存布局,无法完成这些操作。所谓的“void语境”其实是指void*只能用来存储指针值,不能直接操作它指向的内容;必须先将它转换回具体的对象指针类型,才能进行解引用、算术运算等操作。示例代码:
int x = 10; void* ptr = &x; // *ptr = 20; // 错误:无法直接解引用void* int* int_ptr = (int*)ptr; *int_ptr = 20; // 正确:转换为具体类型后解引用
内容的提问来源于stack exchange,提问作者Doohyeon Won
相关产品推荐
相关产品推荐

