1. 程式人生 > >嵌入式C語言入門——關鍵字&巨集

嵌入式C語言入門——關鍵字&巨集

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 型別的指標。