【C語言】10.static&extern,typedef片效,宏红伦,條件編譯,const

  • 全局變量:

    • 程序一啟動就會分配空間淀衣,直到程序結(jié)束色建。
    • 存儲位置在靜態(tài)存儲區(qū)
    • 多個同名的全局變量指向同一塊存儲空間舌缤。
    • 全局變量默認為0
    • 分類:
      • 內(nèi)部變量:只能在這個文件內(nèi)部訪問(加上static關(guān)鍵字)
      • 外部變量:可以在其他文件中訪問的變量,默認所有全局變量都是外部變量某残。(什么都不加或是有extern聲明)
  • static:

    • static對局部變量的作用:

      • 延長局部變量的生命周期,從程序啟動到程序退出,但是它并沒有改變變量的作用域国撵。也就是說,雖然保證它一直存在玻墅,但是該不能用的時候還是不能用介牙。
      • 定義變量的代碼在整個程序運行期間僅僅會執(zhí)行一次。注意澳厢,是定義變量的代碼环础,也就是說只會定義一次囚似。
    • static對全局變量的作用:

      • 由于靜態(tài)全局變量的作用域局限于一個源文件內(nèi),只能為該源文件內(nèi)的函數(shù)公用,因此可以 避免在其它源文件中引起錯誤。

      ?

  • extern:

    • 如果聲明的時候沒有寫extern线得,那系統(tǒng)會自動定義這個變量,并將其初始化為0饶唤。
    • 如果聲明的時候?qū)慹xtern了,那系統(tǒng)不會自動定義這個變量贯钩。
    • 一般時候用extern來聲明變量是其他文件的外部變量募狂。(?)
  • static 與 extern對函數(shù)的作用:

    • C語言默認所有函數(shù)都是外部函數(shù)角雷,可以被其他文件訪問祸穷;而內(nèi)部函數(shù)只能在本文件中訪問。

    • 同樣地勺三,加上static關(guān)鍵字可以聲明和定義一個函數(shù)變?yōu)閮?nèi)部函數(shù)雷滚,extern則可以聲明和定義一個外部函數(shù)。

    • 例:

      // 聲明一個內(nèi)部函數(shù)
      static int sum(int num1,int num2);
      
      // 定義一個內(nèi)部函數(shù)
      static int sum(int num1,int num2)
      {
          return num1 + num2;
      }
      
      // 聲明一個外部函數(shù)
      extern int sum(int num1,int num2);
      
      // 定義一個外部函數(shù)
      extern int sum(int num1,int num2)
      {
          return num1 + num2;
      }
      

      ?

  • typedef:

    • 宏定義也可以完成typedef的工作吗坚,但是宏定義是由預處理完成的祈远,而typedef則是在編譯時完成的,后者更為靈活方便且不易出錯刻蚯。

    • 例:

      typedef int INTEGER;
      
      typedef Integer MyInteger;
      
      typedef char NAME[20]; // 表示NAME是字符數(shù)組類型,數(shù)組長度為20绊含。然后可用NAME 說明變量。
      NAME a; // 等價于 char a[20];
      
      typedef char * String;
      String myString = "hello";
      

      與結(jié)構(gòu)體的聲明定義一樣炊汹,在對結(jié)構(gòu)體使用typedef時躬充,也有三種形式:

      struct Person{
          int age;
          char *name;
      };
      
      typedef struct Person PersonType; // PersonType person; person.age = ...
      
      /////////////////////////////////////////////////////////////////////
      
      typedef struct Person{
          int age;
          char *name;
      } PersonType;
      
      注意這個和結(jié)構(gòu)體在定義的時候直接初始化一個值的區(qū)別,這個是由typedef關(guān)鍵字的讨便,且是別名充甚,而不是一個變量。
      
      /////////////////////////////////////////////////////////////////////
      
      typedef struct {
          int age;
          char *name;
      } PersonType;
      
      省略結(jié)構(gòu)體名霸褒。
      
      /////////////////////////////////////////////////////////////////////
      
      // typedef和指向結(jié)構(gòu)體的指針
      // 首先定義一個結(jié)構(gòu)體并起別名 
        typedef struct { float x; float y; } Point;
      // 起別名
        typedef Point *PP;
      

      與枚舉的聲明定義一樣伴找,在對枚舉使用typedef時,也有三種形式:

      enum Sex{
          SexMan,
          SexWoman,
          SexOther
      };
      typedef enum Sex SexType;
      
      /////////////////////////////////////////////////////////////////////
      
      typedef enum Sex{
          SexMan,
          SexWoman,
          SexOther
      } SexType;
      
      /////////////////////////////////////////////////////////////////////
      
      typedef enum{
          SexMan,
          SexWoman,
          SexOther
      } SexType;
      

      typedef和函數(shù)指針:(重要7狭狻)

      int add(int a, int b) {
          return a + b;
      }
      
      int main() {
          typedef int (*myFunction) (int, int); 
        //注意此時myFunction就是int (*myFunction) (int, int)的別名技矮。表示這是一種指向函數(shù)的指針的類型。
          myFunction p = add; 
          p();
          return 0;
      }
      

      ?

      ?

    • 不帶參數(shù)的宏定義:

      • 格式: #define 標示符 字符串(“字符串”可以是常數(shù)殊轴、表達式衰倦、格式串等。)
      • 宏名的有效范圍是從定義位置到文件結(jié)束旁理。如果需要終止宏定義的作用域樊零,可以用#undef命令。
    • 帶參數(shù)的宏定義:

      • 對帶參數(shù)的宏,在調(diào)用中,不僅要宏展開,而且要用實參去代換形參孽文。

      • #define 宏名(形參表) 字符串

      • #define average(a, b) (a+b)/2

      • 宏名和參數(shù)列表之間不能有空格驻襟,否則空格后面的所有字符串都作為替換的字符串夺艰。

      • 帶參數(shù)的宏在展開時,只作簡單的字符和參數(shù)的替換沉衣,不進行任何計算操作郁副。所以在定義宏時,一般用一個小括號括住字符串的參數(shù)厢蒜。并且計算結(jié)果最好也括起來防止錯誤霞势。

        #define Pow(a) ( (a) * (a) )
        
  • 條件編譯

    • 在很多情況下,我們希望程序的其中一部分代碼只有在滿足一定條件時才進行編譯斑鸦,否則不參與編譯(只有參與編譯的代碼最終才能被執(zhí)行)愕贡,這就是條件編譯。換句話說巷屿,在條件編譯中固以,不滿足條件的會直接被刪去,并不會參與編譯嘱巾。
    • 條件編譯和宏定義經(jīng)常一起使用憨琳。
#define SCORE 67
#if SCORE > 90
    printf("優(yōu)秀\n");
#else
    printf("不及格\n");
#endif

// 條件編譯后面的條件表達式中不能識別變量,它里面只能識別常量和宏定義

#if 條件1
  ...code1...
 #elif 條件2
  ...code2...
 #else
  ...code3...
 #endif

#define SCORE 67
#if SCORE > 90
    printf("優(yōu)秀\n");
#elif SCORE > 60
    printf("良好\n");
#else
    printf("不及格\n");
#endif

  • 注意,條件一般是判斷宏定義而不是判斷變量旬昭,因為條件編譯是在編譯之前做的判斷篙螟,宏定義也是編譯之前定義的,而變量是在運行時才產(chǎn)生的问拘。
  • 一定不要忘記在最后加上 #endif !
  • ifndef 條件編譯指令 與 上面用法類似遍略,不過是條件相反的。
  • 使用條件編譯指令調(diào)試 bug

    #define DEBUG1 0
    #if DEBUG1 == 0 //format是格式控制,##表示可以沒有參數(shù),__VA_ARGS__表示變量 #define Log(format,...) printf(format,## __VA_ARGS__)
    #else
    #define Log(format,...)
    #endif
    
    void test(){
        Log("xxxxx");
    }
    int main(int argc, const char * argv[]) {
        Log("%d\n",10);
        return 0;
    }
    

    這種場景用Log代替printf骤坐,在調(diào)試的時候我們只需要改變宏定義的值绪杏,就可以靈活的控制輸出的語句。比如把DEBUG1設(shè)置為0纽绍,變?yōu)檎{(diào)試狀態(tài)蕾久,那么所有的printf就會替代Log輸出一些信息;把DEBUG1設(shè)置為1拌夏,則關(guān)閉調(diào)試狀態(tài)僧著,則會將Log代替為空,則不會有輸出障簿。

    這里,‘...’指可變參數(shù)盹愚。這類宏在被調(diào)用時,##表示成零個或多個參數(shù),包括里面的逗號,一直到到右括弧結(jié)束為止。當被調(diào)用時,在宏體(macro body)中,那些符號序列集合將代替里面 的VA_ARGS標識符卷谈。

  • 或者用條件指令在調(diào)試階段設(shè)置一些默認值,比如賬號密碼霞篡,等結(jié)束調(diào)試只需要改變宏命令就可以關(guān)閉這個功能世蔗。

  • 在C語言中防止重復導入頭文件的問題:

    #ifndef __xxxxxx__x__
    #define __xxxxxx__x__
    
    #include <stdio.h>
    
    #endif /* defined(__xxxxxx__x__) */
    

    比如這是xxxxx里面的x.h文件端逼,當別的文件導入這個文件的時候(#include),相當于復制這個文件里的內(nèi)容污淋。如果導入了多個文件不小心重復導入了同一個顶滩,那么像上面這樣寫是沒有問題的。因為預處理指令會首先檢查是否定義了

    __xxxxxx__x__
    

    如果沒有定義寸爆,那么就定義礁鲁,并且導入stdio.h文件。

    那么如果下次遇到(這時候都是在預編譯的時候完成的)赁豆,它仍然檢查是否定義了

    __xxxxxx__x__
    

    這次因為剛才我們已經(jīng)定義了仅醇,那么它就會直接結(jié)束這個指令,所以不會重復導入相同的頭文件魔种。

  • 如果出現(xiàn)重復導入循環(huán)(比如a.h文件里導入了b.h析二,而b.h文件里導入了a.h),解決方法:

    如果a.h聲明了一個add方法节预,b.h里面聲明了一個minus方法叶摄,但是此時重復引用循環(huán),編譯器會報錯安拟,那么可以在b.h里面不再導入a.h就不會出現(xiàn)循環(huán)導入的問題蛤吓。但是我又想在b.h里面訪問a.h里面的add方法,但是此時b.h里面并沒有導入a.h糠赦,那么我們就直接將add方法的聲明復制到b.h里面即可会傲。

  • const

    const的作用主要是兩個:

    1. 節(jié)省空間,避免不必要的內(nèi)存分配。(Swift中建議使用let也是這個原因愉棱?)

      #define PI 3.14159 //常量宏
      const doulbe Pi=3.14159; //此時并未將Pi放入ROM中 ...... double i=Pi; //此時為Pi分配內(nèi)存,以后不再分配!
      double I=PI; //編譯期間進行宏替換,分配內(nèi)存
      double j=Pi; //沒有內(nèi)存分配
      double J=PI; //再進行宏替換,又一次分配內(nèi)存! const定義常量從匯編的角度來看,只是給出了對應(yīng)的內(nèi)存地址,而不是象#define一樣給出的是立即數(shù),所以,const定義的常量在程序運行過程中只有一份拷貝,而#define定義的常量在內(nèi)存中有若干個拷貝唆铐。
      

      編譯器通常不為普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成為一個編譯期間的常量,沒有了存儲與讀內(nèi)存的操作,使得它的效率也很高。

    2. 更加安全奔滑。

    使用:

    1. 在變量名前或變量名后艾岂。

      int const x = 2;

      const int x = 2;

    2. 注意用const修飾指針:

      • const int *A; //const修飾指針,A可變,A指向的值不能被修改

      • int const *A; //const修飾指向的對象,A可變,A指向的對象不可變

      • int *const A; //const修飾指針A, A不可變,A指向的對象可變

      • const int *const A;//指針A和A指向的對象都不可變

        ?

      先看“*”的位置

      如果const 在 *的左側(cè) 表示值不能修改,但是指向可以改。

      如果const 在 *的右側(cè) 表示指向不能改,但是值可以改

      如果在“*”的兩側(cè)都有const 標識指向和值都不能改朋其。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末王浴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子梅猿,更是在濱河造成了極大的恐慌氓辣,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袱蚓,死亡現(xiàn)場離奇詭異钞啸,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門体斩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梭稚,“玉大人,你說我怎么就攤上這事絮吵』】荆” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵蹬敲,是天一觀的道長暇昂。 經(jīng)常有香客問我,道長伴嗡,這世上最難降的妖魔是什么急波? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮闹究,結(jié)果婚禮上幔崖,老公的妹妹穿的比我還像新娘。我一直安慰自己渣淤,他們只是感情好赏寇,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著价认,像睡著了一般嗅定。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上用踩,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天渠退,我揣著相機與錄音,去河邊找鬼脐彩。 笑死碎乃,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的惠奸。 我是一名探鬼主播梅誓,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼佛南!你這毒婦竟也來了梗掰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤嗅回,失蹤者是張志新(化名)和其女友劉穎及穗,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绵载,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡埂陆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年苛白,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焚虱。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡丸氛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出著摔,到底是詐尸還是另有隱情,我是刑警寧澤定续,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布谍咆,位于F島的核電站,受9級特大地震影響私股,放射性物質(zhì)發(fā)生泄漏摹察。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一倡鲸、第九天 我趴在偏房一處隱蔽的房頂上張望供嚎。 院中可真熱鬧,春花似錦峭状、人聲如沸克滴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽劝赔。三九已至,卻和暖如春胆敞,著一層夾襖步出監(jiān)牢的瞬間着帽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工移层, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留仍翰,地道東北人。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓观话,卻偏偏與公主長得像予借,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子匪燕,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容