C語言定義和宣告區別
1. 變數的定義和宣告
C語言定義和宣告分為變數的定義和宣告和函式的定義和宣告。由於,函式是具有全域性外部特性,並且函式的定義和宣告區別是很明顯的。其宣告需要返回值型別,函式名和引數列表。而函式的定義需要函式體。所以,很容易區別函式的宣告和定義。對於變數的宣告和定義,就不是那麼明顯。通常變數定義和宣告的區別是有沒有為變數分配記憶體。如果為變數分配記憶體就是定義,否則就是宣告。變數宣告可以出現多次,但定義只能出現一次。
2. 強符號和弱符號
強符號:函式和初始化的全域性變數稱為強符號。
弱符號: 未初始化的全域性變數稱為弱符號。
在程式中,強符號只能出現1次,弱符號會出現多次。如果存在一個強符號和多個弱符號,gcc會選擇強符號。如果存在多個弱符號,會選擇其中一個弱符號作為定義,並分配記憶體。
3. 例項
例項1:多個強符號
三個檔案test11.h, test11.c和test22.c,具體如下:
test11.h:
#ifndef _TEST1_H
#define _TEST1_H
#include <stdio.h>
int i=1; //此處i是定義,是強符號
void printmsg();
#endif
test11.c
#include "test11.h"
void printmsg(){
printf("i=%d\n",i);
}
test22.c
#include "test11.h" int main(){ printmsg(); return 0; }
編譯結果: test11.o:(.data+0x0): multiple definition of `i'
test22.o:(.data+0x0): first defined here
由於出現了多個強符號,所以連結時i重定義了。
例項2:多個弱符號和1個強符號
test11.h
#ifndef _TEST1_H
#define _TEST1_H
#include <stdio.h>
int i;
int i;
void printmsg();
#endif
此處int i,準確來說是定義性宣告,既是定義也是宣告,但沒有初始化,稱為弱符號。
test11.c
#include "test11.h"
void printmsg(){
printf("i=%d\n",i);
}
test22.c
#include "test11.h"
int i=10;
int main(){
printmsg();
return 0;
}
在test33.c對i進行了定義,為強符號。
執行結果:
i=10
當存在多個弱符號和1個強符號時,gcc會選擇強符號,能夠正常的編譯和執行。
例項3:多個弱符號
test11.h
#ifndef _TEST1_H
#define _TEST1_H
#include <stdio.h>
int i;
int i;
void printmsg();
#endif
此處int i,準確來說是定義性宣告,既是定義也是宣告,但沒有初始化,稱為弱符號。
test11.c
#include "test11.h"
void printmsg(){
printf("i=%d\n",i);
}
test22.c
#include "test11.h"
int main(){
printmsg();
return 0;
}
去掉test22.c中的i定義,此時只有多個弱符號。
執行結果:
i=0;
當存在多個弱符號時,gcc會選擇其中一個弱符號作為定義。由於i未初始化,所以儲存在.bss段上,關於變數儲存位置,將在以後文章作詳細介紹。.bss段變數用0進行填充。
注意: 當GCC編譯器看到 int i時,在連結的時候,搜尋源程式,如果定義變數i(有初始值),就把此處的i看成是宣告。如果別處沒有定義,就為此處的i分配空間,即當作定義。
所以: i的轉換過程為:定義性宣告((或稱為暫定義)->定義。
4. 結構體的定義和宣告
struct A{
int i;
int j;
};
稱為型別定義,和變數定義完全不同。即定義一種新的資料型別,類似int型別。如果GCC在編譯時檢查出多個結構體定義,就會報重定義錯誤。即某個程式出現了:
struct A{};
struct A{};
在連結時,如果多個檔案中存在結構體定義,則不會報錯,gcc在連結時只檢查符號,這裡的符號指的是變數和函式之類的符號,而不是型別符號。不會檢查語法。所以在多個檔案中出現結構體定義沒有問題。
struct A A1;
A1稱為結構體變數宣告,和普通變數類似。
總結:
(1)變數定義和宣告的本質區別:是否分配記憶體。
(2)區域性變數不能多次宣告,即在同一程式碼塊(即同一作用域)的區域性變數不可多次宣告。區域性變數不能出現 int i; int i;
(3)需要強調的是,不要在標頭檔案定義全域性變數,容易引起重定義。