【C進階】22、條件編譯分析

bryson發表於2021-12-25

Summary

1)條件編譯的行為類似於C語言中的if else;條件編譯是預編譯指示命令,用於控制是否編譯某段程式碼

2)預編譯器根據條件編譯指令有選擇的刪除程式碼,所以編譯器不知道程式碼分支的存在

3)if else在執行期進行分支判斷,一定會被編進目的碼條件編譯指令預編譯期進行分支判斷,可能產生不同的程式碼段,因此編進目的碼的程式碼段不確定

4)可以通過命令列定義巨集gcc -Dmacro=val file.cgcc -Dmacro file.c

5)#include 本質 是將已經存在的檔案內容插入到當前檔案中;#include的間接包含同樣會產生嵌入檔案內容操作。條件編譯指令#ifndef _FILE_H_ #define _FILE_H #endif可以解決這種標頭檔案重複包含的錯誤。

6)條件編譯指令只是可以在同一個.c檔案中,防止重複包含;如果標頭檔案中有了符號的定義,但是在一個工程的不同c檔案裡都進行了include,這時候編譯這兩個檔案也會有重複定義的錯誤(因為兩份include在各自的c檔案裡都定義了一個global,在同一個全域性作用域裡定義了同名的symbol)。所以,標頭檔案中只宣告、不定義

7)工程中的條件編譯主要用於:不同的產品線共用一份程式碼區分編譯產品的除錯版和釋出版

條件編譯分析

條件編譯的行為類似於C語言中的if...else...
條件編譯是預編譯指示命令,用於控制是否編譯某段程式碼;

1、條件編譯於if...else的區別

  • 預編譯器根據條件編譯指令有選擇的刪除程式碼,所以編譯器不知道程式碼分支的存在
  • if...else語句在執行期進行分支判斷;條件編譯指令預編譯期進行分支判斷
  • 可以通過命令列定義巨集gcc -Dmacro=val file.c或者gcc -Dmacro file.c
#define C1

int main()
{
    #if (C==1)
        printf("if true\n");
    #else
        printf("if false\n");
    #endif    

    return 0;
}
gcc -E test.c -o test.i
// 單步編譯後得到的中間檔案
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.c"

int main()
{
        printf("if true\n");
    return 0;
}

分析:

  • 經過預編譯期的處理後,得到的中間檔案中#if #else #endif都被刪掉了,這也說明了上面的預編譯器根據條件編譯指令有選擇的刪除程式碼,所以編譯器在拿到中間.i檔案後,根本就不知道這些程式碼分支的存在。
  • 也說明,#if #else #endif是在預編譯期進行判斷的,而if else是在執行期才會判斷
  • // 上述程式碼中去掉#define C 1
    // 使用命令來定義巨集
    gcc -DC=1 test.c
    編譯後的執行結果為:if true
    
    gcc -DC test.c
    gcc -DC test.c
    編譯後的執行結果為:if true

使用#ifdef #else #endif進行預編譯分支判斷:

  • #ifdef C
      printf("yes, defined");
    #else
      printf("no, undefined");
    #endif
    
    以上程式碼進行編譯:
    gcc -DC test.c
    輸出:yes, defined
    
    gcc test.c
    輸出:no, undefined

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

  • #include 本質 是將已經存在的檔案內容插入到當前檔案中
  • #include的間接包含同樣會產生嵌入檔案內容操作。
對以上程式碼進行單步編譯
gcc -E test.c -o test.i
gcc -S test.i -o test.s

分析:單步編譯後,在中間.i檔案中,global的定義出現了2次,後續的編譯過程自然會出現重定義的錯誤。
image.png

重複包含的解決方式:在標頭檔案test.h和global.h中加上條件編譯指令

#ifndef _HEADER_FILE_H_
#define _HEADER_FILE_H_
// SRC
#endif

注意條件編譯指令只是可以在同一個.c檔案中,防止重複包含;如果標頭檔案中有了符號的定義,但是在一個工程的不同c檔案裡都進行了include,這時候編譯這兩個檔案也會有重複定義的錯誤(因為兩份include在各自的c檔案裡都定義了一個global,在同一個全域性作用域裡定義了同名的symbol)。所以,標頭檔案中只宣告、不定義
示例程式碼:

依舊是上面的test.c test.h global.h,再加一個test2.c檔案,其中#include "test.h"
執行:gcc test.c test2.c
輸出:multiple definition of 'global'

3、條件編譯工程中的應用

  • if else編譯器處理,必然被編譯進目的碼#if #else #endif預編譯器處理,可以按不同的條件編譯不同的程式碼段,因而會產生不同的目的碼
  • 工程中的條件編譯主要用於:不同的產品線共用一份程式碼區分編譯產品的除錯版和釋出版

本文總結自“狄泰軟體學院”唐佐林老師《C語言進階課程》。
如有錯漏之處,懇請指正。

相關文章