C/C++ extern-static-全局變量-局部變量-宏定義等

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>  
#include "test.h"   

extern i;  
extern void test1();  
extern void test2(); 

int main()  
{                                
   test1();                            
   printf("ok/n");                        
   test2();                            
   printf("%d/n",i);                        
   return 0;                            
}                                

 ×××
 ×××

int i = 10; 
char add1[] = "zly"
char add1[] = "zyj"
extern void test1();
extern void test2();
 ×××
 ×
 ×
 ×
 ×
 ×
 ×
 ×××

test1.c test1.c--diff

#include <stdio.h> 
#include "test.h"   

extern char add1[]; 

void test1()          
{                         
   printf(add1);           
}  

×××

×××

×××

×××

×××

×××

×××

test2.c test2.c--diff

#include <stdio.h>     
#include "test.h"      

extern char add2[];  
extern i;                      

void test2()                    
{                             
   printf(add2);                    
   for (; i > 0; i--)            
       printf("%d-", i);          
}  

×××

×××

×××

×××

×××

×××

×××

×××

×××

      .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修飾全局變量的作用域 只能是本身的編譯單元。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章