sle*_*ske 32 c casting undefined-behavior
作为C的新手,我很困惑何时投射指针实际上是好的.
据我所知,你几乎可以将任何指针类型转换为任何其他类型,编译器将允许你这样做.例如:
int a = 5;
int* intPtr = &a;
char* charPtr = (char*) intPtr;
Run Code Online (Sandbox Code Playgroud)
但是,通常这会调用未定义的行为(尽管它恰好在许多平台上运行).这说,似乎有一些例外:
void*
自由地投射(?)char*
自由地投射(?)(至少我在代码中看过它...).
那么指针类型之间的哪些转换在C 中不是未定义的行为?
编辑:
我试着研究C标准("6.3.2.3指针"一节,见http://c0x.coding-guidelines.com/6.3.2.3.html),但除了一点点之外,我并没有真正理解它void*
.
EDIT2:
只是为了澄清:我明确地只询问"正常"指针,即不是关于函数指针.我意识到强制转换函数指针的规则是非常严格的.事实上,我已经问过:-):如果我转换函数指针,改变参数个数会发生什么
Oli*_*rth 30
基本上:
T *
可以自由转换为a void *
和back(T *
不是函数指针),你将获得原始指针.T *
可以自由地转换为a U *
和back(在哪里T *
和U *
不是函数指针),如果对齐要求相同,你将获得原始指针.如果不是,则行为未定义.注意:( T *
对于非函数指针)始终满足对齐要求char *
.
重要提示:这些规则都没有说明如果将a转换T *
为a U *
然后尝试取消引用它会发生什么.这是标准的一个完全不同的领域.
Oli Charlesworth的优秀答案列出了将指针指向不同类型的指针的所有情况都给出了明确定义的结果.
此外,有四种情况下,转换指针会给出实现定义的结果:
intptr_t
并uintptr_t
用于此目的.结果是实现定义的.在将内存作为连续字节流(即大多数现代平台使用的"线性内存模型")处理的平台上,它通常返回指针指向的内存地址的数值,因此只是字节数.但是,并非所有平台都使用线性内存模型,这就是为什么这是实现定义的原因:-).intptr_t
或者uintptr_t
是通过强制转换指针创建的,则将其转换回相同的指针类型将返回该指针(但可能不再有效).否则结果是实现定义的.请注意,实际解除引用指针(而不是仅读取其值)可能仍然是UB.char*
.然后结果指向对象的最低寻址字节,您可以通过递增指针来读取对象的剩余字节,直到对象的大小.当然,您实际获得的值又是实现定义的......来源:C99标准,第6.3.2.3节"指针"和7.18.1.4"能够保存对象指针的整数类型".
据我所知,指向不同类型指针的指针的所有其他转换都是未定义的行为.特别是,如果你没有强制转换为char
足够大的整数类型,那么将指针指向不同的指针类型也可能总是 UB - 即使没有取消引用它.
这是因为类型可能具有不同的对齐,并且没有通用的,可移植的方式来确保不同类型具有兼容的对齐(除了一些特殊情况,例如有符号/无符号整数类型对).
通常,如果像现在一样,指针本身具有相同的对齐属性,问题不在于转换本身,而在于您是否可以通过指针访问数据.
铸造任何类型T*
来void*
和背部,保证对任何对象类型T
:这是保证给你完全一样的指针返回.void*
是捕获所有对象指针类型.
对于对象类型之间的其他转换,无法保证,通过这样的指针访问对象可能会导致各种问题,例如对齐(总线错误),整数的陷阱表示.不同的指针类型甚至不能保证具有相同的宽度,因此从理论上讲,您甚至可能会丢失信息.
但是,一个应该始终有效的演员是(unsigned char*)
.通过这样的指针,您可以调查对象的各个字节.