T指针,T数组和T指针数组之间的转换是否曾经是未定义的行为?

david.pfx

考虑下面的代码。

#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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

C ++数组上指针数学的未定义行为

LLVM,联合,指针强制转换和未定义的行为

使用从操作符new返回的指针作为数组元素的指针时,它是否是未定义的行为

使用char16_t类型作为char []的数组,然后通过reinterpret_cast <>对其进行转换。我的代码是否有未定义的行为?

硬件SIMD向量指针和相应类型之间的“ reinterpret_cast”运算是否是未定义的行为?

向上转换空指针是否会导致未定义的行为

在已经处理数组基数的指针上应用后减量会引起未定义的行为吗?

是否将reinterpret_cast的派生类指针转换为基类指针未定义的行为?

realloc()悬空指针和未定义的行为

是否将正确对齐和大小的数组转换和访问到未构造的平凡类型未定义行为?

递增指向0大小的动态数组的指针是否未定义?

是否使用无效的指针初始化指针声明符未定义的行为?

在C中使用typedef类型的指针作为原始类型的指针是否存在未定义的行为?

双指针和指针数组之间的区别

减去两个与同一数组未定义行为无关的指针的原理是什么?

将函数指针reinterpret_cast与void(*)()比较是否为未定义行为?

读取指向已删除内存的指针是否存在未定义的行为?

取消引用NULL指针是否被视为未指定或未定义的行为?

是否取消引用等于nullptr的指针的标准未定义行为?

是否在C ++中删除空指针被视为未定义行为?

采取未初始化指针的地址是否为未定义行为?

是否从函数未定义的行为返回取消引用的指针作为引用?

将指针包装在结构中时是否仍会返回指向局部变量的指针仍未定义的行为

数组和指针功能的不同行为

T 数组的函数参数“衰减”指向 T 的指针?

打印空指针未定义行为吗?

用%p打印空指针是未定义的行为吗?

减去指针时的C ++未定义行为

C ++指针怪异的未定义行为