以下爲測試環境:
gcc 4.3.2-1-1
GNU/Linux Debian 5.0
(剛開始使用csdn博客,沒經驗。由於中間調試時間較長,忘了備份,剛寫完,趕緊發了。一看,沒有;再看,還是沒有。揮去憤怒,重寫一篇。望讀者以此爲鑑,及時備份。:-)
1、extern與static
extern 對變量、函數聲明;編譯時可見,即告訴編譯器:“老兄,雖然這個文件裏,我沒有定義,但在別處定義了,你得放過我”。而出於檢查和使用的需要,沒有定義是不能放行的。
函數和變量都默認爲extern的,在鏈接時所有文件可見;更甚的是默認爲不加static和extern即爲定義,這也就帶來的extern顯性聲明的必然性。這將在後面詳細分析。
static,字面意思是靜態限定符,用於三種場合可產生三種效果:
a、作用於局部變量,在函數的生存期其值具有連續性,如何理解,通俗但不準確的表達:被初始化一次,以後每次調用該函數時繼續上次的結果。譬如:
1 #include
<stdio.h>
2
3 #define COUNT
3
4 static stat_count = COUNT -
1;
5 int stat()
6 {
7 static count = COUNT;
8
9 count --;
10 printf("the result in N.O.
%d invacation :
%d./n", stat_count, count);
11 return count;
12 }
13 int main()
14 {
15 while(stat())
16 stat_count --;
17 return 0;
18 }
the result in N.O. 2 invacation : 2.
the result in N.O. 1 invacation : 1.
the result in N.O. 0 invacation : 0.
b、作用於全局變量,只在本文件作用,其相對的是extern。具體比較將在後面分析。
c、作用於函數,僅供本文件其它函數調用,函數和變量都默認爲extern。不是本文重點,不作具體分析。
抽象點,具有三個作用:
隱藏全局可見性;
保持變量內容的持久;
默認初始化爲0。
這個總結見:
http://www.cnblogs.com/dc10101/archive/2007/08/22/865556.html
2、extern全局變量
現代編譯器都按文件編譯,編譯時各個文件獨立檢查語法錯誤。各個全局變量互相透明,可見域在本文件內。
於是即使多個文件有多個定義也不報錯,只是鏈接的時候所有全局變量保存在一個符號表,進行符號的定位。因此,報錯也發生在鏈接階段。
注意到了嗎?link error
也就是說,可見性是在鏈接時擴展的,編譯階段侷限於本文件。
type id;變量和函數默認是extern的,即所有文件可見。
但是默認的不僅是聲明,更是定義。C語言規定,標識符可以有多個聲明,但必須有且僅有一次定義。
聲明爲了讓編譯器知道有哪些符號,什麼類型,以便後面遇到時已經具備一定判斷的意識(譬如,類型檢查,初始化檢查,定義檢查)。
因此,多個文件出現了默認的type id;就是多重定義;如果本意是多個文件共用一個符號,這時需要用到extern顯性指定爲聲明。
extern type id;僅僅是聲明,需要另外定義,且是別的文件裏的定義。
不妨看看編譯器是如何限制的(可以只看紅色標記部分):
1)、
extern char *str_ptr;
str_ptr = "abcd/0";
gcc編譯:
test.c:(.text+0x13): undefined reference to `str_ptr'
2)、
extern char *str_ptr;
char *str_ptr = "abcd/0";
gcc編譯:
test.c:13: error: declaration of ‘str_ptr’ with
no linkage follows extern declaration
test.c:12: error: previous declaration of ‘str_ptr’ was here
3)、
extern
char *str_ptr = "abcd/0";
gcc編譯:
test.c:12: error: ‘str_ptr’ has both ‘extern’ and initializer
4)、
char *str_ptr = "abcd/0";
extern char *str_ptr;
gcc編譯:
test.c:13: error: extern declaration of ‘str_ptr’ follows declaration with
no linkage
test.c:12: error: previous definition of ‘str_ptr’ was here
3、extern作用於指針
不妨作如下測試:
文件1
1 /*
2 * file: test_declaration.c
3 * purpose: declaration of array used by test_extern.c
4 * maninter: hilyhoo AT gmail.com
5 */
6 #include
<stdio.h>
7
8 char str_ptr[] =
"abcd/0";
9 char str_array[] =
"abcd/0";
10
11 int null()
12 {
13 1;
14 return 0;
15 }
文件2
1 /*
2 * file: test_extern.c
3 * input: none
4 * rerult: print 'c' and report a segment error
5 * purpose: test extern point with define array
6 * maninter: hilyhoo AT gmail.com
7 */
8 #include
<stdio.h>
9
10 int main()
11 {
12 extern char *str_ptr;
13 extern char str_array[];
14
15 printf("extern pointer resrult :
%c/n", str_array[2]);
16 printf("extern array resrult :
%c/n", str_ptr[2]);
17
18 return 0;
19 }
gcc編譯運行:
gcc -g test_extern.c test_decleration.c
./a.out
extern pointer resrult : c
段錯誤
gdb調試:
(gdb) b main
Breakpoint 1 at 0x80483c1: file test.c, line 15.
(gdb) p str_ptr
$1 = 0x64636261 <Address 0x64636261 out of bounds>
(gdb) p str_array
$2 = 0x804961a "abcd"
(gdb) r
Starting program: /home/hilyhoo/c/a.out
Breakpoint 1, main () at test.c:15
15 null();
(gdb) s
null () at dec.c:14
warning: Source file is more recent than executable.
14 }
(gdb) p str_ptr
$3 = "abcd/000"
(gdb) x str_ptr
0x8049614 <str_ptr>: 0x64636261
(gdb) s
Line number 15 out of range; dec.c has 14 lines.
(gdb) s
main () at test.c:17
17 printf("extern pointer resrult : %c/n", str_array[2]);
(gdb) x str_array
0x804961a <str_array>: 0x64636261
(gdb) s
extern pointer resrult : c
18 printf("extern array resrult : %c/n", str_ptr[2]);
(gdb) x str_ptr
0x8049614 <str_ptr>: 0x64636261
(gdb) s
Program received signal SIGSEGV, Segmentation fault.
0x080483e8 in main () at test.c:18
18 printf("extern array resrult : %c/n", str_ptr[2]);
上例爲證:
char str_ptr[] = "abcd/0";
extern char *str_ptr;
分別在兩個文件,編譯時獨立,鏈接時,按照聲明使用:
定義:str_ptr,數組,地址0x8049614,內容"abcd/0",16進制0x61、0x62、0x63、0x64
鏈接:str_ptr,指針,地址0x8049614,內容long形整數,16進制0x64636261
str_ptr[2],*(str_ptr + 2),即*(0x64636263),無權訪問,即報斷錯誤。
str_ptr==>"abcd"
0x64
0x63
0x62
0x8049614==>0x61
……
0x64
0x63
0x62
str_ptr + 2 ==>0x63
*(str_ptr + 2)==>?
可以看出:
指針符號是地址的地址;
數組符號,對應整個數組;如果作爲一維數組變量名,則爲其第一個單元的地址。
1)
數組僅僅是一個符號,作爲參數時,由於形參壓棧時,將數組首地址壓進,即以一個空間來存放首地址,就蛻變成指針了;定義多維數組時也會被編譯器解釋成指針。
否則,數組名是不佔單獨的空間的,在符號表中是一個符號,地址爲數組首地址,內容爲首個單元內容。
2)
定義指針時,分配一個空間(我們的體系爲32位,4個字節),其內爲指向的單元的地址。
3、總結
1)、extern如果顯式聲明,在當前文件中在不得定義,且必須在其它鏈接到的文件定義。
(這是在gcc編譯器中,據稱有的編譯器不一樣。比如,可以使用
extern type id = initialize;
因此不敢妄言,恐誤導讀者。)
2)、指針與數組的差別還是很大的,但一般情況數組會蛻變爲指針使用,譬如:
id[offset],編譯器會解釋成*(id + offset),因此一般用來幾乎感覺不到差別。