1. 如何防止頭文件被重複包含(#include)
-- 可以使用條件編譯,所有頭文件都標記:
#ifndef _HEADERNAME_H
#define _HEADERNAME_H
#include ...
#endif
當頭文件第一次被包含時,它被正常處理,符號_HEADERNAME_H被定義爲1,頭文件被再次包含,通過條件編譯,它的內容被忽略;符號_HEADERNAME_H按照被包含頭文件的文件名進行取名,以避免由於其他頭文件使用相同的符號而引起的衝突。
2. 如何放防止變量被重複定義
test.h | test.h--diff |
#ifndef _TEST_H_ #define _TEST_H_ // 防止test.h被重複包含 char add1[] = "zly"; char add2[] = "zyj"; int i = 10; void test1(); void test2(); #endif |
××× ××× extern char add1[]; extern char add2[]; extern int i; ××× ××× ××× |
test.c | test.c--diff |
#include <stdio.h> extern i; |
××× int i = 10; |
test1.c | test1.c--diff |
#include <stdio.h> extern char add1[]; void test1() |
××× ××× ××× ××× ××× ××× ××× |
test2.c | test2.c--diff |
#include <stdio.h> extern char add2[]; void test2() |
××× ××× ××× ××× ××× ××× ××× ××× ××× |
.c文件中只要包含了test.h就會獨立的解釋,然後每個.c文件生成獨立的標示符。在編譯器鏈接時,就會將工程中所有的符號整合在一起,由於文件中有重名變量,於是就出現了重複定義的錯誤;
解決方法:
在.h頭文件中聲明變量並加上extern,不要對變量初始化,在使用該全局變量的.c文件中包含該頭文件,並在.c文件中定義變量。編譯器會爲.c生成目標文件,然後鏈接時,如果該.c文件使用了全局變量,鏈接器就會鏈接到定義變量的.c文件.
3. 變量的聲明有兩種情況,3.1是"定義性聲明(defining declaration),3.2是"引用性聲明(referncingdeclaration)";extern只作引用聲明,不作定義;
3.1.一種是需要建立存儲空間的(定義、聲明)。例如:int a在聲明的時候就已經建立了存儲空間;
3.2.另一種是不需要建立存儲空間的(聲明)。例如:extern int a其中變量a是在別的文件中定義的;4. 鏈接指示符:extern:
4.1:修飾符extern用在變量或者函數的聲明前,用來說明“此變量/函數是在別處定義的,要在此處引用”。
file1.c
---------------------
int a=1;
file2.c
---------------------
#include<stdio.h>
extern int a;
int main(void)
{
printf("%d\",a);
return 0;
}
結果:1;但是如果在file1.c中把int a=1改爲static int a=1;
4.2:C++中extern還可以用來訪問C程序的變量和函數用於指示C或者C++函數的調用規範。比如在C++中調用C庫函數,就需要在C++程序中用extern “C”聲明要引用的函數。這是給鏈接器用的,告訴鏈接器在鏈接的時候用C函數規範來鏈接。主要原因是C++和C程序編譯完成後在目標代碼中命名規則不同,用此來解決名字匹配的問題作爲一種面向對象的語言,C++支持函數重載,而過程式語言C則不支持。函數被C++編譯後在symbol庫中的名字與C語言的不同。例如,假設某個函數的原型爲:
void foo(int x, int y);
該函數被C編譯器編譯後在symbol庫中的名字爲_foo,而C++編譯器則會產生像_foo_int_int之類的名字。_foo_int_int這樣的名字包含了函數名和函數參數數量及類型信息,C++就是靠這種機制來實現函數重載的。 爲了實現C和C++的混合編程,C++提供了C鏈接交換指定符號extern "C"來解決名字匹配問題,函數聲明前加上extern "C"後,則編譯器就會按照C語言的方式將該函數編譯爲_foo,這樣C語言中就可以調用C++的函數了。
a.被extern "C"限定的函數或變量首先是extern類型的
b.extern是C/C++語言中表明函數和全局變量作用範圍(可見性)的關鍵字,該關鍵字告訴編譯器,其聲明的函數和變量可以在本模塊或其它模塊中使用
c.被extern "C"修飾的變量和函數是按照C語言方式編譯和連接的
4.2.1 C++ 調用C
方法a.
#ifdef __cplusplus
extern "C"
#endif
void DeleteStack(Stack stack); //c函數
void PrintStack(Stack stack); /c函數
方法b.
extern "C" {
#include "Stack.h"; /c函數頭文件
}
4.2.2 C 調用C++
C++ 頭文件加上:
extern "C" {
int testmain();
}
C文件include C++頭文件的方式調用
C文件不支持extern "C"
5. static:static可以用來修飾局部變量,全局變量以及函數。static有3個作用:首先static的最主要功能是隱藏,其次因爲static變量存放在靜態存儲區,所以它具備持久性和默認值0。C++中static還具有其它功能,如果在C++中對類中的某個函數用static進行修飾,則表示該函數屬於一個類而不是屬於此類的任何特定對象.如果對類中的某個變量進行static修飾,表示該變量爲類以及其所有的對象所有.可以通過類和對象去調用。對於靜態成員函數,只能訪問靜態成員函數和靜態成員變量,不能訪問非靜態成員函數或者變量。
a.隱藏:同時編譯多個文件時,所有未加static前綴的全局變量和函數都具有全局可見性,其它的源文件也能訪問。如2個源文件a.c和main.
-----------------------------------
a.c:
char a = 'A'; // global variable
void msg()
{
printf("Hello\n");
}
-----------------------------------
main.c:
int main(void)
{
extern char a; // extern variable must be declared before use
printf("%c ", a);
(void)msg();
return 0;
}
-----------------------------------
結果:A Hello
分析:a是全局變量,msg是函數,並且都沒有加static前綴,因此對於另外的源文件main.c是可見的。如果加了static,就會對其它源文件隱藏。例如在a和msg的定義前加上static,main.c就看不到它們了。利用這一特性可以在不同的文件中定義同名函數和同名變量,而不必擔心命名衝突。
b.保持變量內容的持久:存儲在靜態數據區的變量會在程序剛開始運行時就完成初始化,也是唯一的一次初始化。共有兩種變量存儲在靜態存儲區:全局變量和static變量,只不過和全局變量比起來,static可以控制變量的可見範圍.
c.默認初始化爲0:其實全局變量也具備這一屬性,因爲全局變量也存儲在靜態數據區。在靜態數據區,內存中所有的字節默認值都是0x00.
6. 常見疑問:
6.1 static全局變量與普通的全局變量有什麼區別 ?
--全局變量前加static修飾,就變成了靜態全局變量,static全局變量只在當前源文件內有效,而普通全局變量在整個程序的源文件都可以使用;全局變量本身就是靜態存儲方式, 靜態全局變量當然也是靜態存儲方式。static全局變量只初使化一次,防止在其他文件單元中被引用;
6.2 static局部變量和普通局部變量有什麼區別 ?
--局部變量加上static變成靜態變量之後改變了它的聲明週期,全局變量加上static變成靜態變量之後改變了它的作用域;static局部變量只被初始化一次,下一次依據上一次結果值;
6.3 static函數與普通函數有什麼區別?
--static修飾的函數與普通函數相比作用域不同,static的函數只能在當前文件中使用;對於可在當前源文件以外使用的函數,應該在一個頭文件中說明,要使用這些函數的源文件要包含這個頭文件.static函數在內存中只有一份,普通函數在每個被調用中維持一份拷貝。
6.4 extern與static:
--首先extern和static不能同時修飾一個變量;其次static修飾的全局變量聲明與定義同時進行;最後,static修飾全局變量的作用域 只能是本身的編譯單元。