1. 程式人生 > >C語言入門(廿二)之預處理指令、巨集、條件編譯、檔案包含、typedef、const

C語言入門(廿二)之預處理指令、巨集、條件編譯、檔案包含、typedef、const

預處理指令

什麼是預處理指令:

在我們的檔案翻譯成0和1之前做的操作我們稱之為預處理指令。一般情況預處理指令都是以#號開頭的。

巨集定義的格式

不帶引數的巨集定義:

#define 巨集名 值

巨集定義的作用:   
    會在程式翻譯成0和1之前, 將所有巨集名替換為 巨集的值
 

巨集定義在什麼時候替換


 原始碼 --> 預處理 -->彙編 -->二進位制 -->可執行程式

注意:巨集定義後面不要寫分號

巨集定義規範:

一般情況巨集名都大寫, 多個單詞之間用_隔開, 並且每個單詞全部大寫。
有的公司又要求巨集名以k開頭, 多個單詞之間用駝峰命名。

巨集定義的作用域:

從定義的那一行開始, 一直到檔案末尾。
雖然預設情況下巨集定義的作用域是從定義的那一行開始, 一直到檔案末尾. 但是我們也可以通過對應的關鍵字提前結束巨集定義的作用域。(#undef COUNT)

帶引數的巨集定義:

#define SUM(v1, v2) v1+v2

#define 代表要定義一個巨集
 SUM 巨集的名稱
 (v1, v2) 引數, 注意點, 不需要寫資料型別
 v1+v2 用於替換的內容

巨集定義並不會做任何運算, 無論是有引數還是沒有引數都僅僅是在翻譯成0和1之前做一個簡單的"替換"

帶引數的巨集定義注意點


 1.一般情況下建議寫帶引數的巨集的時候, 給每個引數加上一個()
 2.一般情況下建議寫帶引數的巨集的時候, 給結果也加上一個()

如:

#define PF(v1) ((v1)*(v1))

什麼時候用帶引數的巨集定義什麼時候用函式?

 

如果函式內部的功能比較簡單, 僅僅是做一些簡單的運算那麼可以使用巨集定義, 使用巨集定義效率更好, 運算速度更快。
如果函式內部的功能比較複雜, 不僅僅是一些簡單的運算, 那麼建議使用函式。

條件編譯

#define DEBUG 1 // 0是除錯階段 1是釋出階段

#if DEBUG == 0
// 除錯階段
#define NJLog(format, ...) printf(format,## __VA_ARGS__)
#else
// 釋出階段
#define NJLog(format, ...)
#endif

條件編譯和選擇結構if的共同點

都可以對給定的條件進行判斷, 新增滿足或者不滿足都可以執行特定的程式碼

條件編譯和選擇結構if的區別

1.生命週期不同
    if 執行時
    #if 編譯之前
 2.#if需要一個明確的結束符號 #endif
    為什麼需要一個明確的結束符號?
    如果省略掉#endif, 那麼系統就不知道條件編譯的範圍, 那麼會將滿足條件之後的第二個條件之後的所有內容都清除
 3.if會將所有的程式碼都編譯到二進位制中
  #if只會將滿足條件的部分一直到下一個條件的部分 編譯到二進位制中

條件編譯的優點


 1.縮小應用程式的大小

 

應用場景:


 用於除錯和釋出階段進行測試
 除錯階段: 程式寫程式碼的階段
 釋出階段: 上傳到AppStore的階段

 

預處理指令什麼時候執行? 編譯之前 。


變數什麼時候定義? 執行了才會定義。

注意點: 條件編譯不能用來判斷變數, 因為不在同一個生命週期。

一般情況下, 條件編譯是和巨集定義結合在一起使用的。

#include <stdio.h>

#define SCORE 100
//#define COUNT 50

int main(int argc, const char * argv[]) {

    
#ifdef SCORE // 判斷是否定義了後面的巨集
    printf("score\n");
#elif COUNT
    printf("count\n");
#else
    printf("OTHER\n");
#endif
     
#ifndef SCORE // 是不是沒有定義名稱叫做SCORE的巨集
    printf("no score\n");
#else
    printf("score\n");
#endif
    
    
    /*
#if defined(SCORE) // 判斷是否定義了SCORE這個巨集
    printf("score\n");
#else
    printf("no score\n");
#endif
    
#if !defined(SCORE) // 判斷是否沒有定義SCORE這個巨集
    printf("no score\n");
#else
    printf("score\n");
#endif
     */
    

    return 0;
}

檔案包含

#include <>
 <>會先去編譯器環境下查詢, 找不到再去系統的環境下查詢

 #include ""
 ""會先在當前檔案查詢, 找不到再去編譯器環境下查詢, 找不到再去系統的環境下查詢
 
 作用:
 將""或者<>中的內容完全拷貝過來
 
 注意:
 如何正確的編寫.h檔案
 如何防止迴圈拷貝  A拷貝B, B拷貝A
 間接拷貝問題  A拷貝B, B拷貝C, C拷貝D

typedef

什麼是typedef, 它有什麼作用

typedef可以給一個已知的資料型別起別名 (外號)
 
 利用typedef給資料型別起別名的格式:
 
 typedef 原有的資料型別 別名(外號);
 

注意:
 1. typedef不僅能給系統原有的資料型別起別名, 也可以給一個自定義的資料型別起別名。
 2. 利用typedef給資料型別起別名, 並不會生成一個新的資料型別, 僅僅是給原有的型別起了一個別名而已。

typedef可以給指向函式的指標起別名

// functionPotinter == int(*functionPotinter)(int , int)
typedef int(*functionPotinter)(int , int);
int main(int argc, const char * argv[]) {
   
    // 9如何定義變數 :   資料型別 變數名稱;
//    int (*sumP)(int , int ) = sum;
    functionPotinter sumP = sum;
    printf("sum = %i\n", sumP(10 , 20));
    
//    int (*minusP)(int, int) = minus;
    functionPotinter minusP = minus;
    printf("minus = %i\n", minusP(20, 10));
    return 0;
}

可以給指標起別名

// String == char *
typedef char * String;
void test4()
{
    //    char *name = "lnj";
    // 注意: 如果給指標起別名之後, 那麼以後利用別名定義變數就不用再加*了
    String name = "lnj";
    printf("name = %s\n", name);
    
    
}

可以給列舉型別起別名

// 1.先定義列舉型別, 再給列舉型別起別名
/*
 enum Gender
 {
 kGenderMale,
 kGenderFemale
 };
 typedef enum Gender SEX;
 */

// 2.定義列舉型別的同時給列舉型別起別名
/*
 typedef enum Gender
 {
 kGenderMale,
 kGenderFemale
 } SEX;
 */

// 3.定義列舉型別的同時給列舉型別起別名, 並且省略列舉原有型別名稱
typedef enum
{
    kGenderMale,
    kGenderFemale
} SEX;

給結構體型別起別名

// 1.先定義結構體型別, 再給型別起別名
/*
 struct Person
 {
 int age;
 double height;
 char *name;
 };
 // SPerson == struct Person
 typedef struct Person SPerson;
 */

// 2.定義結構體型別的同時, 給結構體型別起別名
/*
 typedef struct Person
 {
 int age;
 double height;
 char *name;
 } SPerson;
 */

// 3.定義結構體型別的同時, 給結構體型別起別名, 並且省略掉原有型別的名稱

 typedef struct
 {
 int age;
 double height;
 char *name;
 } SPerson;

給基本資料型別起別名

// Integer == int
typedef int Integer;
typedef Integer myInt;
// int float doulbe char
void test1()
{
    int num = 10;
    printf("num = %i\n", num);
    
    Integer age = 30;
    printf("age = %i\n", age);
    
    myInt score = 99;
    printf("score = %i\n", score);
}

typedef和巨集定義的區別

一般情況下如果要給資料型別起一個名稱建議用typedef, 不要用define

typedef int myInt;
#define Integer int

typedef char * String;
#define MY_STRING char *

const關鍵字

const修飾的變數內容不可改變,變數變為常量。

    int const num = 10;
    printf("num = %i\n", num);
    //num = 55;//編譯報錯,不能改變變數的值
    printf("num = %i\n", num);

const修飾的指標變數,指標變數指向的地址的內容可以改變,但是指標儲存的地址不能變。

    char const *  name = "lnj";
    printf("name = %s\n", name);
    name = "lk";
    printf("name = %s\n", name);

const可以寫在char這個資料型別的左右都可,習慣寫在最前面。

終於學完了基礎部分,以後多加練習,有些重要的知識點要回頭反覆敲程式碼,不然記不住,同時進度繼續,下一步學oc。