咱們今天來介紹下 C 語言中的 #pragma,#pragma 用於指示編譯器完成一些特定的動做。#pragma 所定義的不少指示字是編譯器特有的,在不一樣的編譯器間是不可移植的。ide
預處理期將忽略它不認識的 #pragma 指令,不一樣的編譯器可能以不一樣的方式解釋同一條 #pragma 指令。通常用法:#pragma parameter。 注意:不一樣的 parameter 參數語法和意義各不相同!性能
#pragma message:a> message 參數在大多數的編譯器中都有類似的實現;b> message 參數在編譯時輸出消息到編譯輸出窗口中;c> message 用於條件編譯中可提示代碼的版本信息。它與 #error 和 #warning 不一樣,#pragma message 僅僅表明一條編譯消息,不表明程序錯誤。學習
下來咱們來分析個示例代碼,代碼以下spa
#include <stdio.h> #if defined(ANDROID20) #pragma message("Compile Android SDK 2.0...") #define VERSION "Android 2.0" #elif defined(ANDROID23) #pragma message("Compile Android SDK 2.3...") #define VERSION "Android 2.3" #elif defined(ANDROID40) #pragma message("Compile Android SDK 4.0...") #define VERSION "Android 4.0" #else #error Compile Version is not provided! #endif int main() { printf("%s\n", VERSION); return 0; }
這段代碼是想利用 #pragma message 定義一條輸出信息,咱們來看看在 gcc 編譯器中輸出什麼3d
咱們能夠看出第一次沒有定義 ANDROID 參數,編譯直接報咱們提示的錯誤。那麼它在輸出信息的時候,也一樣將 #pragma message 輸出了。咱們再在 BCC 編譯器中編譯下,看看輸出是什麼blog
那麼在 BCC 編譯器中咱們將參數寫在後面它還不識別,它編譯以後的結果是沒有 #pragma message,直接顯示後面的信息。由此咱們能夠看到在不一樣的編譯器中,對 message 的處理結果不同。圖片
下來咱們再講講 #pragma once,它是用於保證頭文件只被編譯一次。那麼問題來了,咱們以前講講 #ifndef xxx_h_ #define xxx_h_ #endif 這種用法。它們兩個有什麼區別呢?由於後一種的實現是基於宏參數實現的,因此它每次包含到頭文件時便會進去檢查,因此效率不高。可是由於是宏參數,因此這是 C 語言支持的,在每一個編譯器中都會識別。#pragma once 是編譯器相關的,它不必定被支持。可是由於編譯器一看見它就不去包含了,因此它的效率及其高,所以兩種方式各有利弊。咱們下來作個試驗分析下內存
test.c
編譯器
#include <stdio.h> #include "global.h" #include "global.h" int main() { printf("g_value = %d\n", g_value); return 0; }
global.hit
#pragma once int g_value = 1;
咱們在 gcc 編譯器中編譯下,看看結果如何
咱們看到在 gcc 中沒報錯誤,直接運行。咱們下來在 BCC 中再來編譯下
咱們看到在 BCC 中直接報錯,顯然在 BCC 編譯器中就不支持這種寫法。
那麼下來咱們再來看看 C 語言中的內存對齊,什麼是內存對齊呢?不一樣類型的數據在內存中按照必定規則排列,可是不必定是按照順序的一個接一個的排列。那麼爲何須要內存對齊呢?緣由有這麼幾點:一、CPU 對內存的讀取不是連續的,而是分紅塊讀取的,塊的大小隻能是一、二、四、八、16 ... 字節;二、當讀取操做的數據未對齊,則須要兩次總線週期來訪問內存,所以性能會大打折扣;三、某些硬件平臺只能從規定的相對地址處讀取特定類型的數據,不然會產生異常。#pragma pack 用於指定內存對齊方式。
下面咱們來看個示例代碼,代碼以下
#include <stdio.h> struct Test1 { char c1; short s; char c2; int i; }; struct Test2 { char c1; char c2; short s; int i; }; int main() { printf("sizeof(Test1) = %d\n", sizeof(struct Test1)); printf("sizeof(Test2) = %d\n", sizeof(struct Test2)); return 0; }
咱們看到兩個結構體中的成員變量都同樣,可是位置不一樣。那麼問題來了,它們所佔的內存大小相同嗎?咱們來看看編譯結果
很明顯結果不同,那麼爲何呢?這就是內存對齊了,在計算機內部,默認的是4字節對齊,由於這樣效率最高。咱們能夠指定它們都是1字節對齊,這樣結果就同樣了。#pragma pack 可以改變編譯器的默認對齊方式,下面給個示例
#pragma pack(1) struct Test1 { char c1; short s; char c2; int i; }; #pragma pack()
分別在兩個結構的先後都這樣設置按照 1 字節對齊的方式,咱們再來看看結果如何
這下它們佔的內存大小就同樣了。以前的那種內存分佈以下圖所示
那麼 struct 佔用的內存大小是怎樣計算的呢?第一個成員起始於 0 偏移處,每一個成員按其類型大小和 pack 參數中較小的一個進行對齊;偏移地址必須能被對齊參數整除,結構體成員的大小取其內部長度最大的數據成員做爲其大小;結構體總長度必須爲全部對齊參數的整數倍。編譯器在默認狀況下按照 4 字節對齊。
下來咱們來分析下以前的程序中結構體默認的按照 4 字節怎樣對齊的
#pragma pack(4) struct Test1 { // 內存對齊 起始距離 內存大小 char c1; // 1 0 1 short s; // 2 2 2 char c2; // 1 4 1 int i; // 4 8 4 }; #pragma pack() #pragma pack(4) struct Test2 { // 內存對齊 起始距離 內存大小 char c1; // 1 0 1 char c2; // 1 1 1 short s; // 2 2 2 int i; // 4 4 4 }; #pragma pack()
那麼你們和上面的表對照下,是否是同樣呢。下來咱們再作個實驗,按照8字節對齊試試,代碼以下
#include <stdio.h> #pragma pack(8) struct S1 { // 內存對齊 起始距離 內存大小 short a; // 2 0 2 long b; // 4 4 4 }; struct S2 { // 內存對齊 起始距離 內存大小 char c; // 1 0 1 struct S1 d;// 4 4 8 double e; // 8 16 8 }; #pragma pack() int main() { printf("%d\n", sizeof(struct S1)); printf("%d\n", sizeof(struct S2)); return 0; }
按照咱們的分析,上面應該分別打印出 8 和 24。咱們來看看 gcc 編譯器的結果
咱們看到打印出的是 8 和 20,那麼咱們是否是分析出錯了?彆着急哈,再看看 BCC 編譯器的結果
咱們看到 BCC 編譯器和咱們分析的一致。那麼 gcc 爲何會打印 20 呢?原來在 gcc 編譯器中不支持 8 字節對齊的方式,所以它是按照 4 字節對齊方式進行打印的。那麼最後一個 double 的起始距離就是 12 了,所以結果就是 20 啦。
經過對 #pragma 的學習,總結以下:一、#pragma 用於指示編譯器完成一些特定的動做;二、它所定義的不少指示字是編譯器特有的;三、#pragma message 用於 自定義編譯消息、#pragma once 用於保證頭文件只被編譯一次、#pragma pack 用於指示內存對齊方式。
歡迎你們一塊兒來學習 C 語言,能夠加我QQ:243343083。