C語言之extern作用於指針、數組

以下爲測試環境:

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

這個總結見:

史蒂芬的技術博客static的作用

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),因此一般用來幾乎感覺不到差別。

發佈了28 篇原創文章 · 獲贊 115 · 訪問量 53萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章