在C语言中,将指针转换为不同类型的指针。

4
int i = 42;
int *p1 = &i;
int long *p2 = (long*)p1;

这是否是未定义行为?在C++中,由于某些原因,我认为这是一种实现定义的行为。
我查阅了C标准:
C99 6.3.2.3/7:对象或不完整类型的指针可以转换为指向不同对象或不完整类型的指针。如果结果指针对指向的类型未正确对齐57),则其行为未定义。否则,再次转换的结果应与原始指针比较相等。
57)总的来说,“正确对齐”的概念是传递的:如果指向类型A的指针对指向类型B的指针正确对齐,而指向类型B的指针又对指向类型C的指针正确对齐,则指向类型A的指针对指向类型C的指针正确对齐。
在这里,正确对齐一词在实践中意味着什么?如何知道您是否正在正确执行它而不会进入未定义的行为状态?

1
如果您对 p2 进行解引用,那么这将是未定义的行为。原因是:您违反了严格别名规则。 - Mysticial
int long *p2 这里的 long 是什么意思? - Krishnabhadra
在32位架构上,sizeof(int) == sizeof(long),因此没有区别。在64位架构上,long的另一半可能会有一些垃圾数据,或者甚至可能留下已分配的内存区域,所以再次进行这样的操作是没有意义的。除此之外,这是正确的。 - keltar
1
@Krishnabhadra:int long *p2 是一种合法但略微不正规的写法,等同于 signed long int *p2long *p2long int *p2long int signed *p2(它们之间都是等价的)。 - Jonathan Leffler
在RISC机器的64位环境中,i有很大可能会对齐到32位奇数地址边界,但是long必须对齐到64位地址边界(或者32位偶数地址边界)。因此,在这样的系统上,如果您尝试通过p2读取或修改内存,则会收到SIGBUS错误。 - Jonathan Leffler
1个回答

4

这基本上意味着,如果比如说int对齐到4个字节,而long int对齐到8个字节,那么行为是未定义的。假设你有以下代码:

 0x04     0x08    0x0C    0x10
+------+-------+-------+-------+
|      |       |   i   |       |
+------+-------+-------+-------+

在这种情况下,&i == 0x0C(这是有效的,因为int对齐到4个字节)。当你将其转换为int long*时,指针会被转换为对齐的指针:p2 == 0x08,因为我们理论上的系统将int long对齐到8个字节,所以如果您对p1进行反引用,那么您基本上会读取一个您没有拥有的地址,从而导致未定义行为。

所以,基本上如果两个指针类型的大小不同,比如sizeof int和sizeof long不相等,那么你会得到未定义的行为,对吗? - user963241
据我所知,对齐和大小并不总是相同的。我建议不要这样做。你为什么需要这样做呢? - Luchian Grigore
1
只是好奇在指针类型转换时会导致未定义行为的原因是什么。感谢建议。 - user963241
sizeof(int *) == sizeof(long *) — 指针大小相同。但对齐要求不一定相同。在 RISC 芯片上(而非 Intel),一个 N 字节类型(N = 1、2、4、8)应该在 N 字节边界上对齐。Intel 不强制执行这一点(但如果使用未对齐的指针会有性能损失)。 - Jonathan Leffler
访问类型转换指针会导致未定义的行为。 - Jonathan Leffler
2
我可以检查gcc中类型的对齐要求吗? - user963241

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接