[英]Obtaining a past-the-end pointer using the address of an array
在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 ++)認為是“完整的對象”。 給定一個數組,我們可以在它之后立即派生一個指向數組的指針,取消引用該指針,並對結果對數組的引用使用數組到指針轉換來獲取原始數組的過去指針:
#define END(array) (*(&array + 1))
// ...
int a [42];
do_something (a, END (a));
我的問題是: 在解除引用指向不存在的數組對象的指針時,此代碼是否顯示未定義的行為? 我對C和C ++的最新版本對這段代碼的評價感興趣(不是因為我打算使用它,因為有更好的方法可以實現相同的結果,但因為它是一個有趣的問題)。
我在我自己的代碼中使用了它,作為(&arr)[1]
。
我很確定這是安全的。 指向衰減的數組不是“左值到右值的轉換”,盡管它以左值開始並以右值結束。
這是未定義的行為。
a
的類型array of 42 int
。
&a
是pointer to array of 42 int
。 (注意這不是數組到指針的轉換)
&a + 1
也是pointer to array of 42 int
類型pointer to array of 42 int
5.7p5州:
當向指針添加或從指針中減去具有整數類型的表達式時,結果具有指針操作數的類型。 如果指針操作數指向數組對象的元素,則[...]否則,行為未定義
指針不指向數組對象的元素。 它指向一個數組對象。 所以“否則,行為未定義”是真的。 行為未定義。
它是C中的未定義行為,取消引用指向超出現有對象的指針,除非它本身是包含更多元素的更大對象的一部分。
但是,只要array
是左值,使用&array + 1
的基本思想就是正確的。 (有些情況下數組不是左值。)在這種情況下,這是一個有效的指針操作。 現在要獲取指向第一個元素的指針,您只需將其轉換回基類型。 在你的情況下,將是
(int*)(&array + 1)
指向數組的指針的值保證與指向其第一個元素的指針的值相同,只有類型不同。
不幸的是,我沒有看到一種方法來使這種表達式類型不可知,這樣你就可以把它放在一個通用的宏中,除非你轉換為void*
。 (使用你可以做的gcc typeof
擴展,例如)所以你最好堅持使用便攜式(array)+ARRAYLEN(array)
,這應該適用於所有情況。
在一個奇怪的角落案例中,作為struct
一部分並從函數返回為rvalue的數組不是左值。 我認為標准也允許指針算術,我完全不理解這種結構,所以我不確定它會在那種情況下起作用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.