使用数组的地址获取过期指针

Obtaining a past-the-end pointer using the address of an array

本文关键字:获取 过期 指针 地址 数组      更新时间:2023-10-16

在C和C++中,使用结束指针来编写可以在任意大数组上操作的函数通常很有用。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++)认为它们是"完整的对象"。给定一个数组,我们可以在它之后立即派生一个指向数组的指针,取消对该指针的引用,并在对数组的引用上使用数组到指针的转换,以获得原始数组的结束指针:

#define END(array) (*(&array + 1))
// ...
int a [42];
do_something (a, END (a));

我的问题是:在取消引用指向不存在的数组对象的指针时,此代码是否表现出未定义的行为我很感兴趣的是C和C++的最新修订版对这段代码的看法(不是因为我打算使用它,因为有更好的方法可以获得相同的结果,而是因为这是一个有趣的问题)。

我在自己的代码中使用过它,作为(&arr)[1]

我确信它是安全的。数组到指针的衰减不是"左值到右值的转换",尽管它以左值开始,以右值结束。

这是未定义的行为。

CCD_ 3属于CCD_。

CCD_ 5属于CCD_。(注意,这不是数组到指针的转换)

&a + 1也是pointer to array of 42 int 类型

5.7p5状态:

将具有整型的表达式添加到指针或从指针中减去时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,否则为[…],则行为为未定义

指针不指向数组对象的元素。它指向一个数组对象。因此,"否则,行为是未定义的"是正确的。行为是不明确的。

这是C中未定义的行为,除非指针本身是包含更多元素的更大对象的一部分,否则取消引用指向现有对象之外的指针总是如此。

但是使用&array + 1的基本思想是正确的,只要array是左值。(有些情况下数组不是lvalue。)在这种情况下,这是一个有效的指针操作。现在,要获得指向第一个元素的指针,只需将其强制转换回基类型即可。在你的情况下,那将是

(int*)(&array + 1)

指向数组的指针值保证与指向其第一个元素的指针值相同,只是类型不同。

不幸的是,我看不出有什么方法可以使这样一个表达式类型不可知,这样你就可以把它放在一个通用宏中,除非你转换为void*。(例如,您可以使用gcc typeof扩展)所以你最好坚持使用便携式(array)+ARRAYLEN(array),它应该在所有情况下都能工作。

在一个奇怪的角落情况下,作为struct的一部分并作为函数的右值返回的数组不是左值。我认为这个标准也允许指针算术,但我从来没有完全理解这个结构,所以我不确定它在这种情况下是否有效。