C語言學習第18篇---巨集定義與使用 / 條件編譯使用分析

Allen5G發表於2018-05-24

知識來源主要是陳正衝老師的《C語言深度解剖》及Delphi Tang老師的《C語言剖析》,有興趣的朋友可以看我置頂文章獲取

 

C語言中的巨集定義

#define是前處理器處理的單元實體之一

#define定義的可以出現在程式的任意位置

#define定義之後的程式碼都可以使用這個巨集

 

C語言中的巨集常量

#define定義的巨集常量可以直接使用

#define定義的巨集常量本質為字面量

 

例項1:巨集定義分析

#define ERROR -1
#define PATH1 "D:\test\test.c"
#define PATH2 D:\test\test.c
#define PATH3 D:\test\
test.c
int main()
{
    int err = ERROR;
    char* p1 = PATH1;
    char* p2 = PATH2;
    char* p3 = PATH3;
}

 

巨集定義的表示式:

#define表示式的使用類似函式呼叫

#define表示式可以比函式更加強大

#define變大師比函式更容易出錯

 

實驗2:巨集表示式分析

// #include <stdio.h>
#define _SUM_(a, b) (a) + (b)
#define _MIN_(a, b) ((a) < (b) ? (a) : (b))
#define _DIM_(a) sizeof(a)/sizeof(*a)
int main()
{
    int a = 1;
    int b = 2;
    int c[4] = {0};
    int s1 = _SUM_(a, b);
    int s2 = _SUM_(a, b) * _SUM_(a, b);
    int m = _MIN_(a++, b);
    int d = _DIM_(c);
    // printf("s1 = %d\n", s1);
    // printf("s2 = %d\n", s2);
    // printf("m = %d\n", m);
    // printf("d = %d\n", d);
    return 0;
}

 

巨集表示式與函式的對比:

巨集表示式杯前處理器處理,編譯器不知道巨集表示式的存在

巨集表示式用“實參”完全代替形參,不進行任何運算

巨集表示式沒有任何的“呼叫”開銷

巨集表示式中不能出現遞迴定義

 

強大的內建巨集:

 

 

實驗4:巨集使用綜合

#include <stdio.h>
#include <malloc.h>
#define MALLOC(type, x) (type*)malloc(sizeof(type)*x)
#define FREE(p) (free(p), p=NULL)
#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s)
#define FOREACH(i, m) for(i=0; i<m; i++)
#define BEGIN {
#define END   }
int main()
{
    int x = 0;
    int* p = MALLOC(int, 5);
   
    LOG("Begin to run main code...");
   
    FOREACH(x, 5)
    BEGIN
        p[x] = x;
    END
   
    FOREACH(x, 5)
    BEGIN
        printf("%d\n", p[x]);
    END
   
    FREE(p);
   
    LOG("End");
   
    return 0;
}

 

 

條件編譯的基本概念:

條件編譯的行為類似於C語言中的if ... else...

條件編譯時預編譯指示命令,用於控制是否編譯某段程式碼(這也是專案中經常用到的)

 

條件編譯例項:

// #include <stdio.h>
#define C 1
int main()
{
    const char* s;
    #if( C == 1 )
        s = "This is first printf...\n";
    #else
        s = "This is second printf...\n";
    #endif
    // printf("%s", s);
   
    return 0;
}

 

條件編譯的本質:

預編譯器根據條件編譯指令有選擇的刪除程式碼

編譯器不知道程式碼分支的存在

if ... else ... 語句在執行期進行分支判斷

條件編譯指令在預編譯期進行分支判斷

可以通過命令列定義巨集

gcc -Dmacro = value file.c

或者

gcc -Dmacro file.c

 

實驗:通過命令列定義巨集

//#include <stdio.h>
int main()
{
    const char* s;
    #ifdef C
        s = "This is first printf...\n";
    #else
        s = "This is second printf...\n";
    #endif
    //printf("%s", s);
   
    return 0;
}

 

#include的本質

#include的本質是將已經存在的檔案內容嵌入到當前檔案中

#include的間接包含會產生嵌入檔案內容的操作

 

條件編譯使用例項;

global.h

// global.h
#ifndef _GLOBAL_H_
#define _GLOBAL_H_
int global = 10;
#endif
test.h

// test.h
#ifndef _TEST_H_
#define _TEST_H_
#include "global.h"const char* NAME = "test.h";char* hello_world(){    return "Hello world!\n";}
#endif
// #include <stdio.h>
#include "test.h"
#include "global.h"
int main()
{
    const char* s = hello_world();
    int g = global;
   
    // printf("%s\n", NAME);
    // printf("%d\n", g);
   
    return 0;
}

 

條件編譯可以解決標頭檔案重複包含的編譯錯誤

 

條件編譯的意義:

條件編譯使得我們可以按照不同的條件編譯不同的程式碼段,因而可以產生不同的目的碼

#if... #else #endif 被預編譯器處理,而 if ... else ...語句被編譯器處理,必然被編譯進目的碼

實際工程中條件編譯主要用於以下兩種情況

不同的產品線公用一份程式碼

區分編譯產品的除錯版和釋出版

 

例項分析  產品線區分及除錯程式碼應用

product.h

#define DEBUG 1
#define HIGH  1

 

#include <stdio.h>
#include "product.h"
#if DEBUG
    #define LOG(s) printf("[%s:%d] %s\n", __FILE__, __LINE__, s)
#else
    #define LOG(s) NULL
#endif
#if HIGH
void f()
{
    printf("This is the high level product!\n");
}
#else
void f()
{
}
#endif
int main()
{
    LOG("Enter main() ...");
   
    f();
   
    printf("1. Query Information.\n");
    printf("2. Record Information.\n");
    printf("3. Delete Information.\n");
   
    #if HIGH
    printf("4. High Level Query.\n");
    printf("5. Mannul Service.\n");
    printf("6. Exit.\n");
    #else
    printf("4. Exit.\n");
    #endif
   
    LOG("Exit main() ...");
   
    return 0;
}

 

小結:

相關文章