簡體   English   中英

超過最后一個數組元素末尾的指針是否等於超過整個數組末尾的指針?

[英]Shall a pointer past the end of the last array element compare equal to a pointer past the end of the whole array?

編譯器在編譯代碼時出現分歧

int main()
{
    constexpr int arr[3] = {};
    static_assert((void*)(arr + 3) == (void*)(&arr + 1));
}

在 GCC 和 Clang 中, static_assert不會觸發,MSVC 認為static_assert失敗https://godbolt.org/z/dHgmEN

(void*)(arr + 3) == (void*)(&arr + 1)評估為true ,為什么?

最新標准草案的相關規則:

[介紹對象]

對象可以包含其他對象,稱為子對象。 子對象可以是成員子對象 ([class.mem])、基類子對象 ([class.derived]) 或數組元素。 不是任何其他對象的子對象的對象稱為完整對象。

因此,數組元素是子對象。 示例中的數組是一個完整的對象。


[表達式]

... 比較指針定義如下:

  • 如果一個指針代表一個完整對象的地址,而另一個指針代表另一個完整對象的最后一個元素之后的地址, 79比較的結果是未指定的。

  • 否則,如果 ... 兩者表示相同的地址,則它們比較相等。

79)如 [basic.compound] 中所指定的,為此目的,將不是數組元素的對象視為屬於單元素數組

第一種情況似乎幾乎匹配,但不完全匹配。 兩者都是指向另一個完整對象的最后一個元素之后的指針 - 一個完整對象是數組arr ,另一個是元素為arr的假設單元素數組。 並且也不是指向可能存在於它們各自的數組之后的不相關的完整對象的指針,正如以下 [basic.compound] 引用中的注釋所闡明的那樣。

因此,假設它們代表相同的地址,則應適用另一種情況。


[基礎.化合物]

指針類型的值是指向或超過對象末尾的指針,表示對象43占用的內存([intro.memory])中的第一個字節的地址,或對象結束后的內存中的第一個字節的地址對象分別占用的存儲空間。 [ 注意:超過對象結尾的指針 ([expr.add]) 不被認為指向可能位於該地址的對象類型的無關對象。 指針值在它所表示的存儲到達其存儲持續時間的末尾時變為無效; 參見 [basic.stc]。 — 尾注 ]

出於......比較的目的([expr.rel], [expr.eq]),超過 n 個元素的數組 x 的最后一個元素末尾的指針被認為等效於指向假設數組元素的指針x 的 n不是數組元素的 T 類型對象被認為屬於具有一個 T 類型元素的數組 ...

43)對於不在其生命周期內的對象,這是它將占用或曾經占用的內存中的第一個字節。

arr和僅包含arr的假設數組具有相同的地址和相同的大小。 因此,經過兩個數組的一個元素被認為等效於數組邊界外同一地址的指針。


只是要清楚數組不能包含填充:

[expr.sizeof]

...當應用於一個類時,結果是該類的對象中的字節數,包括將該類型的對象放置在數組中所需的任何填充。 將 sizeof 應用於可能重疊的子對象的結果是類型的大小,而不是子對象的大小。 應用於數組時,結果是數組中的總字節數。 這意味着 n 個元素的數組的大小是元素大小的 n 倍。


讓我們澄清轉換為void*不應該改變指針值,因此不應該改變相等性。

[轉化率]

類型為“指向 cv T 的指針”的純右值,其中 T 是對象類型,可以轉換為類型為“指向 cv void 的指針”的純右值。 指針值 ([basic.compound]) 不會因這種轉換而改變。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM