2010-06-29 33 views
11

可能重複:
Take the address of a one-past-the-end array element via subscript: legal by the C++ Standard or not?我可以把數組中的一個過去的元素的地址?

int array[10]; 

int* a = array + 10; // well-defined 
int* b = &array[10]; // not sure... 

是最後一行的有效與否?

+2

杜佩傳說中的http://stackoverflow.com/questions/988158/採取一個一個地址過去的末端數組元素通過下標合法的由 – 2010-06-29 22:42:40

+0

@約翰內斯 - 你是對的,投票結束。 – Omnifarious 2010-06-29 23:26:40

回答

19

是的,您可以將地址放在數組的末尾,但不能對其進行解引用。對於10個物品的數組,array+10將起作用。有幾次(由委員會等人)爭論過&array[10]究竟是否確實導致了未定義的行爲(如果確實如此,是否應該這樣做)。它的底線是,至少根據現行標準(包括C和C++)它會正式導致未定義的行爲,但如果有一個編譯器實際上不起作用,那麼任何參數都沒有人能夠找到或引用它。

編輯:這一次我的記憶是對了一半 - 這是(部分)的正式Defect Report該委員會,並至少有一些委員會成員(例如,湯姆·梅)認爲措辭已經改變,因此會不是導致未定義的行爲。 OTOH,DR的日期從2000年開始,狀態仍然是「起草」,因此可以質疑它是否確實是固定的,或者很可能是(我沒有通過N3090/3092瞭解)。

但是,在C99中,顯然是而不是未定義的行爲。

+1

很好的答案。作爲歷史的一個可能有趣的地方,我實際上看到一個C編譯器無法正確解引用&array [N]。惠普RTE採用HP-1000主機。它在我的數組上方設置了一個內存「fence」,所以引用地址不存在於程序的地址空間中。 甚至在1989 C標準之前,所以我認爲可以肯定地說這個編譯器已經不在了。 – 2010-06-29 23:46:44

+0

該DR的語言不在N3092中。雖然我現在找不到它們,但是有幾個_closed_ DR依賴於提議的解決方案中的「空左值」語言來解決該缺陷... – 2010-07-11 01:20:55

1

不是。它沒有定義。 array[10]相當於*(array + 10)。換句話說,你已經取消了一個無效指針。

+3

但是他沒有對其進行解引用... – 2010-06-29 21:33:25

+1

@clmh對於'&* p'在技術上是否構成無效操作,或者是解析引用,然後是地址 - 有一個明確的爭論。 – 2010-06-29 21:39:41

+1

'&array [10]'是合法的,不會取消引用元素。 – 2010-06-29 21:49:53

2

array[10]相當於*(array + 10)(也相當於10[array]),所以&array[10]的作用就像從*(array+10)除去*。任何體面的編譯器都應該發出相同的代碼(和相同的警告)。

8

正如其他答案已經表明,表達式array[10]相當於*(array + 10),這似乎導致剛剛超過數組末尾的元素的未定義解引用。然而,表達&array[10]相當於&*(array + 10),和C99標準明確(6.5.3.2地址和間接運算符):

一元運算符&返回其操作數的地址。如果操作數的類型爲''type'',則結果的類型爲'',指向類型''。如果操作數是一個運算符的一元運算符,則該運算符和運算符都不會被評估,並且結果就好像兩個都被省略一樣,除了運算符上的約束仍然適用且結果不是左值。類似地,如果操作數是[]運算符的結果,則對[]隱含的&運算符和一元*都不進行評估,結果就好像刪除了運算符&並將[]運算符更改爲+運算符。

因此,沒有任何關於&array[10]未定義 - 沒有取消引用操作發生。

+5

C99不是C++。 – 2010-06-29 22:48:09

-2

這實際上是一個相當簡單的事情來回答。

所有你正在做的是指針數學,它沒有什麼無效的。如果您嘗試以某種方式使用結果指針,那麼取決於您的進程是否可以訪問該指針所指向的內存,如果有的話,它的權限是什麼?

foo.cc:

#include <iostream> 

using namespace std; 
int main() { 
     int array[10]; 
     int *a = array + 10; 
     int *b = &array[10]; 
     int *c = &array[3000]; 

     cerr << "Pointers values are: " << a << " " << b << " " << c << endl; 
     return 0; 
} 

編譯:

g++ -Werror -Wall foo.cc -o foo 

應產生NO警告,因爲INT * B = &陣列[10]是有效

實際上這樣是我添加的後續行,只是指出有關指針數學的一些事情。

不僅會foo的編譯,它會順利運行:

./foo 
    Pointers are: 0x7fff1d356e68 0x7fff1d356e68 0x7fff1d359d20 

基本上數組語法FOO [IDX]是短切和指針數學的清潔表示。

如果對c99有一些疑惑,標準不會影響這個數學運算並且定義清晰。

在C99同樣的事情:

#include <stdio.h> 

int main() { 
     int array[10]; 
     int *a = array + 10; 
     int *b = &array[10]; 
     int *c = &array[3000]; 

     fprintf(stderr, "Pointers are: %p, %p, %p\n" , a , b , c); 
     return 0; 
} 

然後:

gcc -std=c99 -Wall -Werror -o foo foo.c 

./foo 

輸出:

Pointers are: 0x7fff2c7d22c8, 0x7fff2c7d22c8, 0x7fff2c7d5180 
相關問題