使用数组地址获取超过结束指针的 在C和C++中,使用过去的指针来编写可以在任意大数组上运行的函数通常是有用的。C++给出了 STD::Eng/过载,以使这更容易。另一方面,在C中,我发现这样定义和使用宏并不少见: #define ARRAYLEN(array) (sizeof(array)/sizeof(array[0])) // ... int a [42]; do_something (a, a + ARRAYLEN (a));
我还看到了一个指针算术技巧,用于让此类函数在单个对象上运行:使用数组地址获取超过结束指针的 在C和C++中,使用过去的指针来编写可以在任意大数组上运行的函数通常是有用的。C++给出了 STD::Eng/过载,以使这更容易。另一方面,在C中,我发现这样定义和使用宏并不少见: #define ARRAYLEN(array) (sizeof(array)/sizeof(array[0])) // ... int a [42]; do_something (a, a + ARRAYLEN (a));,c++,c,c++11,undefined-behavior,c11,C++,C,C++11,Undefined Behavior,C11,我还看到了一个指针算术技巧,用于让此类函数在单个对象上运行: int b; do_something (&b, &b + 1); 我突然想到,数组也可以做类似的事情,因为它们被C(我相信,C++)视为“完整对象”。给定一个数组,我们可以在数组之后立即派生一个指向数组的指针,取消对该指针的引用,并对数组的结果引用使用数组到指针的转换,以获得原始数组的结束指针: #define END(array) (*(&array + 1)) // ... int a [42];
int b;
do_something (&b, &b + 1);
我突然想到,数组也可以做类似的事情,因为它们被C(我相信,C++)视为“完整对象”。给定一个数组,我们可以在数组之后立即派生一个指向数组的指针,取消对该指针的引用,并对数组的结果引用使用数组到指针的转换,以获得原始数组的结束指针:
#define END(array) (*(&array + 1))
// ...
int a [42];
do_something (a, END (a));
我的问题是:<强>在对一个不存在的数组对象的引用中,这个代码是否显示了未定义的行为?< /强>我感兴趣的是C++和C++对这个代码的最新修订。(不是因为我打算使用它,因为有更好的方法可以达到同样的效果,而是因为这是一个有趣的问题)。我在自己的代码中使用了它,如
(&arr)[1]
我很确定这是安全的。数组到指针的衰减不是“左值到右值的转换”,尽管它以左值开始,以右值结束。这是未定义的行为
a
的类型为数组42 int
&a
类型为指向42 int数组的指针(注意,这不是数组到指针的转换)
&a+1
也是指向42 int数组的指针类型
5.7p5指出:
将具有整数类型的表达式添加到指针或从指针中减去时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,则[…]否则,行为未定义
指针不指向数组对象的元素。它指向数组对象。因此“否则,行为未定义”是真的。行为未定义。在C中是未定义的行为,如果指针指向现有对象之外,则取消引用该指针始终是正确的,除非它本身是包含更多元素的较大对象的一部分
但是使用&array+1
的基本思想是正确的,只要array
是左值(有些情况下数组不是左值)。在这种情况下,这是一个有效的指针操作。现在要获得指向第一个元素的指针,只需将其转换回基类型。在您的情况下,这将是
(int*)(&array + 1)
指向数组的指针的值保证与指向其第一个元素的指针的值相同,只是类型不同
不幸的是,我没有看到一种方法可以使这样一个表达式类型不可知,这样你就可以把它放在一个通用宏中,除非你强制转换为void*
(例如,使用gcctypeof
扩展你可以做)
因此,您最好坚持使用可移植的(数组)+ARRAYLEN(数组)
,即在所有情况下都应该工作
在一个奇怪的例子中,作为结构的一部分并从函数返回右值的数组不是左值。我认为标准也允许在这里使用指针算法,但我从来没有完全理解过这种构造,所以我不确定它在这种情况下是否会工作。我很惊讶这还没有答案,bUT在做了大量的阅读之后,我认为一致的是指向一个数组的末尾是一个不能被撤销的有效指针。引用的C标准中的一个通俗的通则是:代码>5.5.6/8</Cord>。在C++中,它是代码> 5.7 / 5 < /代码>。如果您感兴趣,这里是a。@ ReMaabel[1],这似乎表明代码不是LE。gal.为了指针运算的目的,C(不确定C++)认为一个完整的对象等同于区段1数组的唯一元素(在这种情况下,int[42]
类型的数组是int[1][42]
类型数组的唯一元素).6.5.8
明确禁止取消引用超过结束指针的指针(在已计算的上下文中),例如由&array+1
形成的指针,该指针正在被取消引用。@StuartOlsen:但它是数组到指针的转换(通常称为衰减)计算上下文?它不使用对象的值,只使用对象的地址。@BenVoigt我相信计算上下文规则是指正在计算的间接寻址(即,*ptr
出现在sizeof
/alignof
/\u alignof
表达式之外)。“如果结果指向数组对象的最后一个元素后一个元素,则该元素不得用作所计算的一元*
运算符的操作数。”“-6.5.6.8
,N1570@StuartOlsen例如,在C++中,绑定引用不评估它绑定到的对象。(在5.3.1节第1段注意),任一标准允许您通过指向对象的指针间接进行引用。(例如,指针&arr+1
)如果生成的左值(引用)未进行左值到右值的转换?两种语言都引用“对象”由指针引用,这似乎意味着在指针所描述的位置必须有一个适当类型的对象才能取消引用它。我能找到的最好结果是C允许在被运算符的地址立即取消时发生这种间接寻址(例如,&*NULL
)StuART: C++标准中包含了(4P8)一个注释,并给出操作符的<代码>和<代码>地址的操作数……但它不限于<代码>和/代码>运算符,它适用于没有出现LValueRealRealchange的任何地方。