提问人:alessio solari 提问时间:7/25/2023 最后编辑:chqrliealessio solari 更新时间:7/26/2023 访问量:125
将 C 指针强制转换为其他类型时要记住的规则
Rules to keep in mind when casting a C pointer to a different type
问:
我已经使用 C 指针一段时间了,一切都按预期工作。但是,现在我遇到了 ISO 标准,该标准说“如果生成的指针未正确对齐指向类型,则行为未定义”。
这让我不禁要问自己:
这是什么意思?
我们如何安全地将指针投射到其他类型?
我发现这个 C 代码示例据说是不正确的,因为它与更严格对齐的指针类型有关:
int main() {
char c = 'x';
int *ip = (int *)&c;
char *cp = (char *)ip;
return 0;
}
- 更严格对齐的指针类型是什么意思?
答:
- 更严格对齐的指针类型是什么意思?
C 2018 6.2.8 5说道:
对齐方式的顺序是从较弱到较强或更严格的对齐方式。更严格的对齐方式具有较大的对齐方式值。满足对齐要求的地址也满足任何较弱的有效对齐要求。
如果您没有“对齐”的规范,C 2018 6.2.8 1 说:
完整的对象类型具有对齐要求,这些要求对可以分配该类型对象的地址施加了限制。对齐方式是实现定义的整数值,表示可以分配给定对象的连续地址之间的字节数...
实际上,这意味着具有对齐要求 A 的对象必须从地址 A 的倍数开始。
- 这是什么意思?
考虑一些值为 P 的指针。也就是说,P 是指针指向的地址。出于这个答案的目的,我们将 P 视为内存空间中的完全解析地址。(实际指针可以用对各种基址或段的引用来表示,也可以用这些基址或段开头的偏移量来表示。当指针转换为指向其他对象类型的指针时,新对象类型具有一些对齐要求 A。如果 P 是 A 的倍数,则 P 与对象类型正确对齐,并且转换会生成一些新的有效指针。(由于 C 中的其他规则,它不一定是可用于访问新类型对象的指针。如果 P 不是 A 的倍数,则 C 标准不定义程序的行为。转换可能不会生成有效的指针,可能会生成调整为正确对齐的指针,程序可能会中止,或者程序的行为方式可能出乎您的意料。
- 我们如何安全地将指针投射到其他类型?
仅当您知道源指针与目标类型正确对齐时,才将指针转换为指向新对象类型的指针。
鉴于 转换为 通常不安全,因为 a 可以被赋予任何起始地址,但在现代 C 实现中通常有四个字节的对齐要求,即使在旧的 C 实现中也至少需要两个字节。因此,存在不合适对齐的危险。char c;
&c
int *
char
int
&c
int
类型的对齐要求因 C 实现而异。如果指针指向对齐类型与新类型一样严格或更严格的对象,则您知道可以安全地转换该对象。否则,C 实现可能会提供测试对齐的方法。通常,转换为 a 会提供地址的表示形式,可以测试该地址以查看它是否是所需对齐方式的倍数。一个类型的对齐要求可以通过计算来获得。uintptr_t
_Alignof(type-name)
评论
char*
int*
int*
char*
void *
void
我们如何安全地将指针投射到其他类型?
除了其他答案中涵盖的方面外......
警惕在对象指针之间进行转换,例如 、 、 ...和函数指针行。void *
int *
unsigned char *
int (*p)()
函数指针可能比所有对象指针更宽(或很少更少)。因此,像 这样的代码可能会丢失重要信息。void *p = main
通常,在明确限制之前,请避免将函数指针转换为对象指针 - 即使如此,也请考虑其他方法。
Erik 已经彻底涵盖了问题 (1) “它是什么意思?”和问题 (3) “更严格对齐的指针类型是什么意思?”。
至于。。。
- 我们如何安全地将指针投射到其他类型?
埃里克基本上说,如果,从任何类型转换为任何类型都是安全的,这是绝对正确的,也是最一般和最全面的答案。但是,您似乎正在寻找一些实用的规则,显式检查对齐要求在运行时并没有真正有用,如果在开发过程中完成,则会产生特定于实现的结果。您可能会发现这些规则更实用:T *
U *
alignof(U) <= alignof(T)
安全
- 从任何对象指针类型强制转换为
void *
- 从任何对象指针类型强制转换为 ,或者
char *
signed char *
unsigned char *
- 在指向类型的指针之间强制转换,这些类型仅在符号和/或类型限定符上有所不同。
- 从指向结构类型的指针转换为指向结构的第一个成员类型的指针
- 从指向联合类型的指针转换为指向联合的任何成员类型的指针
- 从指向数组类型的指针转换为指向数组元素类型的指针,反之亦然
- 如果可以通过一个或多个安全的中间转换安全地从 to 转换为类型,则直接从类型转换为类型。
T *
U *
T *
U *
- 在任意两种函数指针类型之间强制转换
- 给定通过一系列指针转换获得的指针值,这些指针转换中的任何一个都不调用未定义的行为,无论其安全性是否可事先证明,都要转换回原始类型或任何中间类型。
p
p
请注意,指针转换的安全性仅与转换本身有关。取消引用由安全(在我们正在讨论的意义上)转换生成的指针,或者对于函数指针,通过转换后的指针调用指向的函数不一定安全。
评论
int *ip = (int *)&c;
可以访问未由 定义的内存。char c
int
char