-
全局變量:
- 程序一啟動就會分配空間淀衣,直到程序結(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的作用主要是兩個:
-
節(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)存的操作,使得它的效率也很高。
更加安全奔滑。
使用:
-
在變量名前或變量名后艾岂。
int const x = 2;
const int x = 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 標識指向和值都不能改朋其。
-