昨天同事寫了乙個程式,**如下:
檔案a:
char * p1 = "hello";
char p2 = "world";
檔案b:
#include
extern char * p1;
extern char * p2;
int main()
用gcc編譯以後問結果如何,回答:輸出hello和world,結果hello輸出了,world輸出的時候發生段錯誤,最後將puts(p2);語句改為puts(&p2);之後正確輸出,哎,顏面掃地啊,慚愧,慚愧,靜下心來好好琢磨一番,才有了這篇文章的誕生。
分析1:
在檔案a中定義的陣列和指標被編譯器彙編為如下**:
.file "a.c"
.globl p1
.section .rodata
.lc0:
.string "hello"
.data
.align 4
.type p1, @object
.size p1, 4
p1:.long .lc0
.globl p2
.type p2, @object
.size p2, 6
p2:.string "world"
.ident "gcc: (gnu) 4.1.2 20080704 (red hat 4.1.2-46)"
.section .note.gnu-stack,"",@progbits
注意以p1和p2定義的差別,p1實際是乙個long大小的空間,裡面儲存的是.lc0標籤的位置,而.lc0標籤的位置代表了字串的位置,所以在檔案b中使用以下語句: puts(p1);p1代表的是乙個指標,通過指標裡面的值可以找到字串的位置,所以正確輸出。
在使用puts(p2)語句的時候,其實p2在記憶體中的位置裡面直接放的是字串,從char p2 = "world";到extern char * p2;其實發生了乙個轉變,編輯將p2當成純粹的乙個指標來使用,即在檔案b中p2和p1在編譯器的眼中是一樣的,但其實定義的時候並不一樣,這樣從p2的位置取字串的位址然後通過這個位址取的字串顯然會崩潰,因為p2中本來放的就是字串,將字串當成位址再取字串,鬼知道會指到**去,所以崩潰。
但是使用puts(&p2);語句的時候能正確,是因為編譯器將p2標籤的位址傳遞puts函式,這樣從p2的位址取的p2標籤的位置,然後再p2標籤的地方獲取字串就正確了,檔案b生成的彙編**可以看出差距:
// puts(p1)語句生成的彙編**
movl p1, %eax
movl %eax, (%esp)
call puts
// puts(p2)語句生成的彙編**
movl $p2, %eax
movl %eax, (%esp)
call puts
分析2
所以,通過分析1可以看出來這樣的extern使用會出問題,但在函式中使用則沒有問題,如下所示:
void foo(char p)
void foo2(char * p)
對foo和foo2函式傳遞指標和資料名沒問題,因為編譯器自己在裡面會處理,有興趣的可以看看生成的彙編**。
總結:在乙個檔案中定義字元陣列,如果在別的檔案中extern成指標的時候,感覺其實隱含了乙個型別轉換,但這個轉換雖然沒有警告,但不代表是正確的,程式執行的時候會崩潰!
c語言陣列與指標的定義 例子
對以下變數給出定義 1 int a 乙個整型數 2 int a 乙個指向整型的指標 3 int a 乙個指向指標的指標 4 int b 10 乙個包含10個整型數的陣列 5 int b 10 乙個包含10個指標的陣列,指標所指向的是整型數 6 int b 10 乙個指向包含10個整型數陣列的指標 7...
C語言指標陣列與陣列指標
初學者總是分不出指標陣列與陣列指標的區別。其實很好理解 指標陣列 首先它是乙個陣列,陣列的元素都是指標,陣列佔多少個位元組由陣列本身決定。它是 儲存指標的陣列 的簡稱。陣列指標 首先它是乙個指標,它指向乙個陣列。在32 位系統下永遠是佔4 個位元組,至於它指向的陣列佔多少位元組,不知道。它是 指向陣...
C語言指標 指標與陣列
1 指標與一維陣列 2 指標與二維陣列 3 指標陣列與陣列指標 指標裡面最難的一種就是指標與陣列了,對於指標和二維陣列來說這裡面的關係比較複雜。牽涉到編譯器的原理和編譯過程。1 指標與一維陣列 指標是指向乙個記憶體的位址,一維陣列的陣列名代表一塊記憶體 這個陣列 的首位址,和陣列首元素的位址一樣,但...