嵌入式C語言入門——關鍵字&巨集
阿新 • • 發佈:2018-12-01
C關鍵字
預處理
巨集函式
- 避免了函式的入棧、出棧、跳轉等開銷,可以提高程式執行效率
- 多次呼叫,會是程式碼變的龐大,以空間換時間
- 避免替換錯誤需要新增括號
#define AAD(x, y) (x + y)
#define MAX(x, y) ( (x > y) ? (x) : (y) )
巨集的有效範圍
- 從巨集定義的位置開始到檔案結束或者取消巨集定義
- 不做作用域檢測
- 不做型別檢測
void test()
{
#define NUM 10
}
int main()
{
printf("%d\n", SUM);
}
撤銷巨集
#undef SUM
內建巨集
printf("程式碼在 %d 行\n", __LINE__);
printf("程式碼編譯的時間%s %s\n", __DATE__, __TIME__);
printf("檔名 %s\n", __FILE__);
printf("函式名%s\n", __FUNCTION__);
條件編譯ifdef
#ifdef 識別符號 程式段1 #else 程式段2 #endif 它的功能是:如果識別符號已被#define命令定義過,則對程式段1進行編譯; 否則對程式段2進行編譯。如果沒有程式段2(它為空),本格式中的#else可以沒有,#endif不可或缺
#include <stdio.h>
int main()
{
#ifdef _DEBUG
printf ("正在使用DEBUG模式編譯程式碼。。。\n");
#else
printf ("正在使用Release模式編譯程式碼。。。。\n");
#endif
return 0;
}
編譯的時候增加巨集:gcc -D_DEBUG
#if 常量表達式
程式段1
#else
程式段2
#endif
它的功能是:如果常量表達式的值為真(非0),則對程式段1進行編譯;
否則對程式段2進行編譯。因此可以使程式在不同條件下編譯,完成不同的功能。
#include <stdio.h>
// window _WIN32
// Linux __linux__
int main()
{
#if (_WIN32)
{
printf("this is window system\n");
}
#elif (__linux__)
{
printf ("this is linux system\n");
}
#else
{
printf ("unkown system\n");
}
#endif
#if 0
code
#endif
return 0;
}
#與##
- #用於在與編譯期間將巨集引數轉化為字串
#include <stdio.h>
#define SQR(x) printf ("The square of "#x" is %d \n", ((x)*(x)))
int main()
{
SQR(8);
return 0;
}
- ##用於在預編譯期間粘連兩個符號
#include <stdio.h>
#define CREATEFUN(name1, name2)\
void name1##name2()\
{\
printf ("%s called\n", __FUNCTION__);\
}
CREATEFUN(my, Function);
int main()
{
myFunction();
return 0;
}
關鍵字
Static
Static修飾區域性變數
在區域性靜態變數前面加上關鍵字static,該區域性變數便成了靜態區域性變數。靜態區域性變數有以下特點:
(1)該變數在全域性資料區分配記憶體
(2)如果不顯示初始化,那麼將被隱式初始化為0
(3)它始終駐留在全域性資料區,直到程式執行結束
(4)其作用域為區域性作用域,當定義它的函式或語句塊結束時,其作用域隨之結束
Static修飾全域性變數
在全域性變數前面加上關鍵字static,該全域性變數變成了全域性靜態變數。
全域性靜態變數有以下特點:
(1)在全域性資料區內分配記憶體
(2)如果沒有初始化,其預設值為0
(3)該變數在本檔案內從定義開始到檔案結束可見,即只能在本檔案內使用
Static修飾函式
在函式的返回型別加上static關鍵字,函式即被定義成靜態函式。
靜態函式有以下特點:
(1) 靜態函式只能在本原始檔中使用
(2) 在檔案作用域中宣告的inline函式預設為static
說明:靜態函式只是一個普通的全域性函式,只不過受static限制,他只能在所在檔案內使用,不能在其他檔案內使用。
extern
extern可置於變數或者函式前,以表示變數或者函式的定義在別的檔案中,提示編譯器遇到此變數或函式時,在其它模組中尋找其定義。
const
const只是不能通過修飾的變數改變空間的值,不代表空間的值不能改變
const修飾一般變數
一般變數是指簡單型別的只讀變數。這種只讀變數在定義時,修飾符const可以用在型別說明符前,也可以用在型別說明符後。例如:
int const i = 2;
const int i = 2;
const修飾陣列
定義或說明一個只讀陣列
int const a[5] = {1,2,3,4,5};
const int a[5] = {1,2,3,4,5};
const修飾指標
const int * p; // p可變,p指向的物件不可變
int const * p; // p可變,p指向的物件不可變
int * const p; // p不可變,p指向的物件可變
const int * const p; // 指標p和p指向的物件都不可變
這裡給出一個記憶和理解的方法:
先忽略型別名(編譯器解析的時候也是忽略型別名),我們看const離哪個近,離誰近就修飾誰。
const (int) *p //const 修飾*p,p是指標,*p是指標指向的物件,不可變。
(int) const * p; //const 修飾*p,p是指標,*p是指標指向的物件,不可變。
( int) * const p;//const 修飾p,p不可變,p指向的物件可變
const ( int)* const p; // 前一個const修飾*p,後一個const修飾p,指標p和p指向的物件都不可變
const修飾函式引數
const修飾也可以修飾函式的引數,當不希望這個引數值在函式體內被意外改變時使用,例如:
`void Fun(const int *p);`
告訴編譯器*p在函式體中不能改變,從而防止了使用者的一些無意的或錯誤的修改。
const修飾函式返回值
const修飾符也可以修飾函式的返回值,返回值不可被改變。例如:
`const int Fun(void);`
typedef
使用關鍵字 typedef 可以為型別起一個新的別名,語法格式為:
typedef oldName newName;
oldName 是型別原來的名字,newName 是型別新的名字。
需要強調的是,typedef 是賦予現有型別一個新的名字,而不是建立新的型別。為了“見名知意”,請儘量使用含義明確的識別符號,並且儘量大寫。
typedef給結構體型別定義別名
typedef struct stu
{
char name[20];
int age;
char sex;
} STU;
STU 是 struct stu 的別名,可以用 STU 定義結構體變數:
STU body1,body2;
它等價於:
struct stu body1, body2;
typedef給指標型別定義別名
typedef int (*PTR_TO_ARR)[4];
表示 PTR_TO_ARR 是型別int * [4]的別名,它是一個二維陣列指標型別。接著可以使用 PTR_TO_ARR 定義二維陣列指標:
PTR_TO_ARR p1, p2;
按照類似的寫法,還可以為函式指標型別定義別名:
typedef int (*PTR_TO_FUNC)(int, int);
PTR_TO_FUNC pfunc;
typedef與#define區別
typedef 在表現上有時候類似於 #define,但它和巨集替換之間存在一個關鍵性的區別。
正確思考這個問題的方法就是把 typedef 看成一種徹底的“封裝”型別,宣告之後不能再往裡面增加別的東西。
1) 可以使用其他型別說明符對巨集型別名進行擴充套件,但對 typedef 所定義的型別名卻不能這樣做。如下所示:
#define INTERGE int
unsigned INTERGE n; //沒問題
typedef int INTERGE;
unsigned INTERGE n; //錯誤,不能在 INTERGE 前面新增 unsigned
2) 在連續定義幾個變數的時候,typedef 能夠保證定義的所有變數均為同一型別,而 #define 則無法保證。例如:
#define PTR_INT int *
PTR_INT p1, p2;
經過巨集替換以後,第二行變為:
int *p1, p2;
這使得 p1、p2 成為不同的型別:p1 是指向 int 型別的指標,p2 是 int 型別。
相反,在下面的程式碼中:
typedef int * PTR_INT
PTR_INT p1, p2;
p1、p2 型別相同,它們都是指向 int 型別的指標。