在C和C ++中,使用past-the-end指针来编写可以在任意大型数组上运行的函数通常很有用。 C ++提供std::end
重载使这更容易。另一方面,在C中,我发现看到定义和使用宏的情况并不罕见:
#define ARRAYLEN(array) (sizeof(array)/sizeof(array[0]))
// ...
int a [42];
do_something (a, a + ARRAYLEN (a));
我还看过一个指针算术技巧,用于让这些函数对单个对象进行操作:
int b;
do_something (&b, &b + 1);
我发现类似的事情可以用数组来完成,因为它们被C(并且我相信,C ++)认为是完整的对象。"给定一个数组,我们可以在它之后立即派生一个指向数组的指针,取消引用该指针,并对结果对数组的引用使用数组到指针的转换,以获得原始数组的过去指针: / p>
#define END(array) (*(&array + 1))
// ...
int a [42];
do_something (a, END (a));
我的问题是:在取消引用指向不存在的数组对象的指针时,此代码是否显示未定义的行为?我对C和C的最新版本感兴趣C ++不得不说这个代码(不是因为我打算使用它,因为有更好的方法来实现相同的结果,但因为它是一个有趣的问题)。
答案 0 :(得分:1)
我在我自己的代码中使用了它,(&arr)[1]
。
我很确定这是安全的。指向衰减的数组不是“左值到右值转换”,尽管它以左值开始并以右值结束。
答案 1 :(得分:1)
这是未定义的行为。
a
的类型为array of 42 int
。
&a
的类型为pointer to array of 42 int
。 (注意这不是数组到指针的转换)
&a + 1
也属于pointer to array of 42 int
5.7p5州:
当向指针添加或从指针中减去具有整数类型的表达式时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,则[...]否则,行为未定义
指针不指向数组对象的元素。它指向一个数组对象。所以“否则,行为未定义”是真的。行为未定义。
答案 2 :(得分:0)
它是C中的未定义行为,取消引用指向现有对象之外的指针,除非它本身是包含更多元素的更大对象的一部分。
但是,只要&array + 1
是左值,使用array
的基本思路就是正确的。 (有些情况下数组不是左值。)在这种情况下,这是一个有效的指针操作。现在要获取指向第一个元素的指针,您只需将其转换回基类型。在您的情况下将是
(int*)(&array + 1)
指向数组的指针的值保证与指向其第一个元素的指针的值相同,只有类型不同。
不幸的是,我没有看到一种方法来使这种表达式类型不可知,这样你就可以把它放在一个通用宏中,除非你转换为void*
。 (使用gcc typeof
扩展程序,例如)
所以你最好坚持使用便携式(array)+ARRAYLEN(array)
,那个应该适用于所有情况。
在一个奇怪的角落案例中,作为struct
一部分并且从函数返回为rvalue的数组不是左值。我认为这个标准也允许在这里使用指针算法,但是我从来没有完全理解这个结构,所以我不确定它是否适用于那种情况。