2010-05-23 63 views
14

我是一個編程課程的助教,有的學生做這種類型的錯誤:「地址(&)被忽略的數組/地址是gcc?

char name[20]; 
scanf("%s",&name); 

這是不是因爲他們在學習奇怪......令人驚訝的是,除了海灣合作委員會警告,代碼工作(至少這部分)。我一直在試圖理解和我寫了下面的代碼:

void foo(int *v1, int *v2) { 
    if (v1 == v2) 
    printf("Both pointers are the same\n"); 
    else 
    printf("They are not the same\n"); 
} 

int main() { 
    int test[50]; 
    foo(&test, test); 
    if (&test == test) 
    printf("Both pointers are the same\n"); 
    else 
    printf("They are not the same\n"); 
} 

編譯和執行:

$ gcc test.c -g 
test.c: In function ‘main’: 
test.c:12: warning: passing argument 1 of ‘foo’ from incompatible pointer type 
test.c:13: warning: comparison of distinct pointer types lacks a cast 
$ ./a.out 
Both pointers are the same 
Both pointers are the same 

任何人都可以解釋爲什麼他們不一樣?

我懷疑這是因爲我無法獲得數組的地址(因爲我不能有& &x),但在這種情況下,代碼不應該編譯。

編輯:我知道一個數組本身與第一個元素的地址是一樣的,但我認爲這與這個問題無關。例如:

int main() { 
    int a[50]; 
    int * p = a; 
    printf("%d %d %d\n", p == a, p == &a[0], &p[0] == a); 
    printf("%d %d %d\n", p == &a, &p == a, &p == &a); 
} 

打印:

$ ./a.out 
1 1 1 
1 0 0 

我不明白爲什麼第二行以1開始。

+1

你的編輯表明你認爲有一些指針與數組一起存儲。但事實並非如此。一個數組只包含*它的元素。 – 2010-05-24 00:09:39

+0

我知道一個數組只是元素,這就是爲什麼我認爲它不應該編譯。 – dbarbosa 2010-05-24 00:14:18

回答

22

在您的例子中,陣列test是50 ints塊。所以它看起來像這樣:

| int | int | ... | int | 

當您將一元&操作到一個數組,你得到的陣列的地址。就像你將它應用於其他任何東西一樣,真的。所以&test是指向的50 ints該塊的指針:

(&test) -----------> | int | int | ... | int | 

指向的50個整數的數組的指針已鍵入int (*)[50] - 這是的&test類型。

當你只是在它不是要麼sizeof或unary- &運營商的操作數的任何地方使用的名稱test,其被評估爲指針,以它的第一個元素。所以,你傳遞給foo()test計算結果爲指針test[0]元素:

(test) -----------------\ 
         v 
(&test) -----------> | int | int | ... | int | 

你可以看到,這些都被指向同一個地址 - 儘管&test指向整個數組,並test指向到數組的第一個元素(它只顯示在那些值不同的類型中)。

+0

+1;有時候一張圖片勝過千言萬語:-) – 2010-05-24 00:13:23

+6

我喜歡ascii藝術:)現在用這張圖片,很容易解釋爲什麼在'&test'中加入'1'會超出數組範圍,並將'1'加入'test'只會移動到第一個元素之外。 – 2010-05-24 00:16:41

+0

之後,我不得不改變「接受的答案」! – dbarbosa 2010-05-24 00:27:49

9

其實它們是不同的,它們至少沒有相同的類型。

但是在C中,數組的地址與數組中第一個元素的地址相同,這就是爲什麼「他們沒有不同」,基本上,他們指向相同的東西。

+0

「該陣列的地址與陣列中第一個元素的地址相同」 我一直都知道數組本身(而不是*數組地址)與數組的地址相同第一個元素,即'test'與'&test [0]'完全相同。我剛剛檢查過K&R,這就是寫在那裏的...... 我將編輯我的問題以添加此內容。 – dbarbosa 2010-05-23 23:38:40

+3

@WhirlWind:不,它不是*系統相關的。將地址 - 運算符應用於數組具有明確定義的語義 - 它必須產生數組本身的地址。該數組被定義爲基類型的連續對象序列,因此數組的地址和第一個對象的地址是相同的地方(但具有不同的類型)。 – caf 2010-05-24 00:01:14

-2

我相信這是一個gcc優化。想想看。

  • &test點的test
  • test點的test&test[0]
  • [0]所述第一元件中的地址是相同的(在大多數情況下),爲*

所以根據這&test可能不同於test但gcc優化將此消除,因爲在這一點上沒有額外的間接級別的目的。

+2

這不是一個gcc優化 - 它是C標準所要求的行爲。 – caf 2010-05-24 00:01:57

+0

看起來@埃爾茲像我一樣誤解了&測試的含義。 – dbarbosa 2010-05-24 00:43:19

+1

@dba哇我是。爲此接受的答案完全清除它 – Earlz 2010-05-24 00:45:17

5

如果定義像

char name[20]; 

name陣列是隱式轉換爲char*,但&name是類型char (*)[20](指針的20個字符的陣列)的。地址是一樣的。

檢查地址(&name + 1)。它與&name的差異是sizeof(char [20])

+0

謝謝,你更容易理解詹姆斯麥克奈利斯的答案。 – dbarbosa 2010-05-24 00:22:36

+0

所有的樂趣都是我的:) – 2010-05-24 00:32:19

2

在大多數情況下,數組的名稱評估爲其初始元素的地址。兩個例外是當它是sizeof的操作數或一元&的操作數。

一元&給出了它的參數的地址。數組的地址與其初始元素的地址相同,因此(void*)&array == (void*)array將始終爲真。

array當轉換爲指向其初始元素的指針時,其類型爲T *&array的類型是T (*)[n],其中n是數組中元素的數量。因此,

int* p = array;  // Works; p is a pointer to array[0] 
int* q = &array;  // Doesn't work; &array has type int (*)[10] 
int (*r)[10] = &array; // Works; r is a pointer to array 
+0

謝謝,我只知道sizeof的例外。 – dbarbosa 2010-05-24 00:19:52