考虑下面的代码。
#include <stdio.h>
int main() {
typedef int T;
T a[] = { 1, 2, 3, 4, 5, 6 };
T(*pa1)[6] = (T(*)[6])a;
T(*pa2)[3][2] = (T(*)[3][2])a;
T(*pa3)[1][2][3] = (T(*)[1][2][3])a;
T *p = a;
T *p1 = *pa1;
//T *p2 = *pa2; //error in c++
//T *p3 = *pa3; //error in c++
T *p2 = **pa2;
T *p3 = ***pa3;
printf("%p %p %p %p %p %p %p\n", a, pa1, pa2, pa3, p, p1, p2, p3);
printf("%d %d %d %d %d %d %d\n", a[5], (*pa1)[5],
(*pa2)[2][1], (*pa3)[0][1][2], p[5], p1[5], p2[5], p3[5]);
return 0;
}
上面的代码在C中编译并运行,产生预期的结果。所有指针值都相同,所有int值也一样。我认为对于任何类型T,结果都是相同的,但int
最容易使用。
我承认最初惊讶于取消引用指针到数组会产生相同的指针值,但是在反思时,我认为这只是我们知道和喜欢的数组到指针衰减的反面。
[编辑:注释掉的行会触发C ++中的错误并触发C中的警告。在这一点上,我发现C标准含糊不清,但这不是真正的问题。]
在这个问题中,它被称为未定义行为,但我看不到它。我对吗?
如果要查看,请在此处编码。
在我写完上面的文章之后,我就意识到这些错误是因为C ++中只有一个级别的指针衰减。需要更多的解除引用!
T *p2 = **pa2; //no error in c or c++
T *p3 = ***pa3; //no error in c or c++
在我设法完成此编辑之前,@ AntonSavin提供了相同的答案。我已经编辑了代码以反映这些更改。
这是仅C语言的答案。
C11(n1570)6.3.2.3 p7
指向对象类型的指针可以被转换为指向不同对象类型的指针。如果结果指针未正确对齐*),则该行为未定义。否则,当再次转换回时,结果应与原始指针进行比较。
*)通常,“正确对齐”的概念是可传递的:如果类型的指针与类型
A
的指针B
正确对齐,而类型C
的指针又对类型的指针A
正确对齐,则类型的指针对类型的指针正确对齐。类型的指针C
。
该标准有点含糊,如果我们将此类指针(除了使用严格的别名)用于转换后的其他操作,会发生什么,但是其意图和广泛的解释是,此类指针应比较相等(并且具有相同的数值,例如,当转换为时,它们也应该相等。uintptr_t
例如,考虑一下(void *)array == (void *)&array
(转换为char *
而不是void *
明确保证可以工作)。
T(*pa1)[6] = (T(*)[6])a;
很好,指针已正确对齐(与相同&a
)。
T(*pa2)[3][2] = (T(*)[3][2])a; // (i)
T(*pa3)[1][2][3] = (T(*)[1][2][3])a; // (ii)
当且仅当T[6]
有相同对齐要求T[3][2]
,并且一样T[1][2][3]
,(i)
和(ii)
分别是安全的。对我来说,它们不能,这听起来很奇怪,但我无法在标准中找到保证它们具有相同对齐要求的保证。
T *p = a; // safe, of course
T *p1 = *pa1; // *pa1 has type T[6], after lvalue conversion it's T*, OK
T *p2 = **pa2; // **pa2 has type T[2], or T* after conversion, OK
T *p3 = ***pa3; // ***pa3, has type T[3], T* after conversion, OK
忽略由于int *
在printf
期望中传递而导致的UB void *
,让我们看一下参数中的next表达式printf
,首先是定义的表达式:
a[5] // OK, of course
(*pa1)[5]
(*pa2)[2][1]
(*pa3)[0][1][2]
p[5] // same as a[5]
p1[5]
请注意,此处严格混叠不是问题,不涉及错误键入的左值,我们T
以as访问T
。
以下表达式取决于越界指针算法的解释,也可以使用更为宽松的解释(允许container_of
,数组展平,使用的“结构修改”char[]
等);更严格的解释(允许使用可靠的运行时边界检查实现指针算术和解引用,但是不允许进行container_of
数组展平(但不一定进行数组“提升”,执行的操作),结构修改等)使它们不确定:
p2[5] // UB, p2 points to the first element of a T[2] array
p3[5] // UB, p3 points to the first element of a T[3] array
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句