C++ 指向数组重叠端的指针
这个代码正确吗C++ 指向数组重叠端的指针,c++,c,arrays,language-lawyer,C++,C,Arrays,Language Lawyer,这个代码正确吗 int arr[2]; int (*ptr)[2] = (int (*)[2]) &arr[1]; ptr[0][0] = 0; 显然,ptr[0][1]访问arr的范围之外是无效的 注意:毫无疑问,ptr[0][0]指定了与arr[1]相同的内存位置;问题是是否允许我们通过ptr访问该内存位置。还有一些示例,说明表达式确实指定了相同的内存位置,但不允许以这种方式访问内存位置 注2:< /强>也考虑 **PTR=0;。正如Marc van Leeuwen所指出的,
int arr[2];
int (*ptr)[2] = (int (*)[2]) &arr[1];
ptr[0][0] = 0;
显然,ptr[0][1]
访问arr
的范围之外是无效的
注意:毫无疑问,
ptr[0][0]
指定了与arr[1]
相同的内存位置;问题是是否允许我们通过ptr
访问该内存位置。还有一些示例,说明表达式确实指定了相同的内存位置,但不允许以这种方式访问内存位置
<强>注2:< /强>也考虑<代码> **PTR=0;<代码>。正如Marc van Leeuwen所指出的,
ptr[0]
相当于*(ptr+0)
,但是ptr+0
似乎与指针算术部分不符。但是通过使用*ptr
可以避免这种情况。尝试在这里回答代码在常用编译器上工作的原因:
int arr[2];
int (*ptr)[2] = (int (*)[2]) &arr[1];
printf("%p\n", (void*)ptr);
printf("%p\n", (void*)*ptr);
printf("%p\n", (void*)ptr[0]);
所有行在常用的编译器上打印相同的地址。因此,ptr
是一个对象,其*ptr
表示与常用编译器上的ptr
相同的内存位置,因此ptr[0]
实际上是指向arr[1]
的指针,因此arr[0][0]
是arr[1]
。因此,代码为arr[1]
赋值
现在,让我们假设一个反常的实现,其中指向数组的指针(注意:我说的是指向数组的指针,即&arr
,其类型为int(*)[]
,而不是arr
,其含义与&arr[0]
相同,且类型为int*
)是指向数组中第二个字节的指针。然后,使用char*
算法从ptr
中取消引用ptr
与从ptr
中减去1相同。对于结构和联合,可以保证指向此类类型的指针与指向此类类型的第一个元素的指针相同,但在这种情况下,没有为数组找到此类保证(即,指向数组的指针与指向数组的第一个元素的指针相同)事实上@fuzzxl计划提交一份关于该标准的缺陷报告。对于这种反常的实现,*ptr
即ptr[0]
将与&arr[1]
不同。事实上,在RISC处理器上,它会由于数据对齐而导致问题
一些额外的乐趣:
int arr[2] = {0, 0};
int *ptr = (int*)&arr;
ptr[0] = 5;
printf("%d\n", arr[0]);
那代码行吗?它打印5张
更有趣的是:
int arr[2] = {0, 0};
int (*ptr)[3] = (int(*)[3])&arr;
ptr[0][0] = 6;
printf("%d\n", arr[0]);
这样行吗?它打印6
这显然应该奏效:
int arr[2] = {0, 0};
int (*ptr)[2] = &arr;
ptr[0][0] = 7;
printf("%d\n", arr[0]);
这取决于你所说的“正确”是什么意思。您正在对ptr进行强制转换,以
arr[1]
。在C++中,这可能是一个代码> RealTytCase。C和C++是语言(大多数时候)认为程序员知道他在做什么。这段代码有缺陷与它是有效的C/C++代码无关
你不违反任何标准(我所见)。
< P> C++(我使用草案N429)<代码> [DCL .ARD] / 7 特别说明,如果下标结果是数组,它立即被转换为指针。也就是说,在ptr[0][0]
ptr[0]
中,首先将其转换为int*
,然后才对其应用第二个[0]
。所以它是完全有效的代码
对于C(C11草案N1570)
6.5.2.1/3
也有相同的规定。不是一个答案,而是一个我似乎无法很好地表达的评论,除非是一堵文字墙:
给定的数组保证连续存储其内容,以便可以使用指针对其进行“迭代”。如果我可以取一个指向数组开头的指针,然后依次递增该指针,直到我访问了数组的每个元素,那么这肯定会说明数组可以作为一个由任何类型的数组组成的序列来访问
当然,以下各项的结合:
1) 数组[x]将其第一个元素存储在地址“Array”处
2) 指向它的指针的连续增量足以访问下一项
3) 数组[x-1]遵循相同的规则
那么,至少可以将地址“array”视为类型array[x-1]而不是类型array[x],这应该是合法的
此外,考虑到关于连续的点以及指向数组中元素的指针必须如何工作,那么将数组[x]的任何连续子集分组为数组[y]肯定是合法的,其中y统一处理的是单个元素。因此,我认为讨论访问特定元素是有效的还是定义的才有意义。是的,这是正确的代码。为C++14引用N4140: [expr.sub]/1。。。表达式
E1[E2]
与*((E1)+(E2))
[expr.add]/5。。。如果指针操作数和结果都指向同一数组对象的元素,或超过数组对象最后一个元素的元素,则计算不应产生溢出;否则,行为是未定义的
这里没有溢流<代码>&*(*(ptr))==&ptr[0][0]==&arr[1]
对于C11(N1570),规则相同。§6.5.2.1和§6.5.6让我给出一个不同的意见:这是(至少在C++中)未定义的行为,原因与该问题相关的另一个问题中的原因大致相同 首先让我澄清一下前总统
typedef int two_ints[2];
typedef int* int_ptr;
typedef two_ints* two_ints_ptr;
two_ints arr;
two_ints_ptr ptr = (two_ints_ptr) &arr[1];
int_ptr temp = ptr[0]; // the two_ints value ptr[0] gets converted to int_ptr
temp[0] = 0;