Summary
1)#pragma
用於 指示編譯器
完成一些特定的動作;#pragma
所定義的很多指示字是編譯器特有的
,所以在不同的編譯器間是不可移植
的
前處理器
將忽略
它不認識的#pragma指令- 不同的編譯器可能以不同的方式解釋同一條#pragma指令
- 一般用法:
#pragma parameter
,不同的parameter引數語法和意義各不相同。
2)#pragma message
在編譯時輸出資訊到編譯輸出視窗;和#error、#warning不同,#pragma message僅僅表示一條提示資訊,不代表錯誤。(vc、bcc和gcc三款編譯器行為不同)
3)#pragma once
用於保證標頭檔案只被編譯一次
; #pragma once
是編譯器相關
的,不一定被支援。(vc和gcc支援,bcc不支援)
4)#ifndef...#define...#endif
也可以用來防止標頭檔案被重複包含,和#pragma once
有何不同?
#ifndef
方式是C語言支援
的,用在各個編譯器都可以;如果一個.h被include了n次
,預編譯器就會判斷n次
這個標頭檔案是否已經包含了。#pragma once
方式是編譯器相關
的,不一定所有編譯器都支援;預編譯器只會處理一次
,後面不再判斷。
5)什麼是記憶體對齊?
不同型別的資料在記憶體中按照一定的規則排列
,不一定是順序的
一個接一個的排列
6)為什麼需要記憶體對齊?
- CPU對記憶體的讀取不是連續的,而是
分成塊讀取
的,塊的大小隻能是2的冪
,1、2、4、8...位元組 - 當讀取操作的資料未對齊,則需要兩次
匯流排週期
來訪問記憶體,因此效能
會大打折扣 - 某些硬體平臺只能從規定的相對地址處讀取特定型別的資料,否則會產生硬體異常
7)編譯器預設的對齊方式為4位元組對齊
, #pragma pack(n)
可以調整編譯器的預設對齊方式(vc和bcc編譯器支援8位元組對齊,但是gcc不支援)
8)記憶體對齊的規則
:
·#pragma分析
#pragma
用於 指示編譯器
完成一些特定的動作;#pragma
所定義的很多指示字是編譯器特有的,所以在不同的編譯器間是不可移植
的
前處理器
將忽略
它不認識的#pragma指令- 不同的編譯器可能以不同的方式解釋同一條#pragma指令
- 一般用法:
#pragma parameter
,不同的parameter引數語法和意義各不相同。
1、#pragma message
- message引數在大多數的編譯器中都有相似的實現
- message引數在編譯時輸出訊息到編譯輸出視窗中
message用於條件編譯中可提示程式碼的版本資訊
#include <stdio.h> #if defined(ANDROID20) #pragma message("Compile Android SDK 2.0...") #define VERSION "ANDROID 2.0" #elif defined(ANDROID30) #pragma message("Compile Android SDK 3.0...") #define VERSION "Android 3.0" #elif defined(ANDROID40) #pragma message("Compile Android SDK 4.0...") #define VERSION "Amdroid 4.0" #else #error Compile version is not provided! #endif int main() { printf("%s\n", VERSION); return 0; }
不同編譯器的編譯結果: bcc編譯器:bcc32 -DANDROID30 test.c 編譯輸出:Compile Android SDK 3.0... vc編譯器:cl -DANDROID30 test.c 編譯輸出:Compile Android SDK 3.0... gcc編譯器:gcc -DANDROID30 test.c 編譯輸出:note: #pragma message: Compile Android SDK 4.0... 三款編譯器的輸出說明:#pragma message會輸出提示資訊,但是行為相似,可能具體實現不同
單步編譯的中間檔案: gcc -DANDROID30 -E test.c -o test.i # 12 "test.c" #pragma message("Compile Android SDK 3.0...") # 12 "test.c" # 20 "test.c" int main() { printf("%s\n", "Android 3.0"); return 0; }
注意:和#error、#warning不同,#pragma message
僅僅代表一條編譯資訊
,不代表程式錯誤。
2、#pragmae once
#pragma once
用於保證標頭檔案只被編譯一次
#pragma once
是編譯器相關
的,不一定被支援
#ifndef...#define...#endif
也可以用來防止標頭檔案被重複包含,和#pragma once
有何不同?
#ifndef
方式是C語言支援
的,用在各個編譯器都可以;如果一個.h被include了n次
,預編譯器就會判斷n次
這個標頭檔案是否已經包含了。#pragma once
方式是編譯器相關
的,不一定所有編譯器都支援;預編譯器只會處理一次
,後面不再判斷。// test.h #pragma once int aa = 1; // test.c #include <stdio.h> #include "test.h" #include "test.h" int main() { return 0; }
不同編譯器的編譯結果: bcc編譯器:bcc32 test.c 編譯輸出:Variable 'aa' is initialized more than once vc編譯器:cl test.c 編譯輸出:成功 gcc編譯器:gcc test.c 編譯輸出:成功 分析:#pragma once預處理指示字,vc和gcc都可以識別,但是bcc就無法識別,直接忽略了,然後aa就會被定義2次,造成編譯錯誤
由於#ifndef會被判斷n次,但是#pragma once又不是所有編譯器都支援的,為了提高效率
,可以這兩種方式同時使用
:
#ifndef _TEST_H_
#define _TEST_H_
#pragma once
// code
#endif
3、#pragma pack和記憶體對齊
3.1 什麼是記憶體對齊?
不同型別的資料在記憶體中按照一定的規則排列
,不一定是順序的
一個接一個的排列
3.2 為什麼需要記憶體對齊?
- CPU對記憶體的讀取不是連續的,而是
分成塊讀取
的,塊的大小隻能是2的冪
,1、2、4、8...位元組 - 當讀取操作的資料未對齊,則需要兩次
匯流排週期
來訪問記憶體,因此效能
會大打折扣 - 某些硬體平臺只能從規定的相對地址處讀取特定型別的資料,否則會產生硬體異常
3.3 #pragma pack
編譯器預設的對齊方式為4位元組對齊
#pragama pack
可以調整編譯器的預設對齊方式
#include <stdio.h> #pragma pack(1) struct Test1 { char c1; short s; char c2; int i; }; #pragma pack() #pragma pack(1) struct Test2 { char c1; char c2; short s; int i; }; #pragma pack() int main() { printf("sizeof(Test1) = %d\n", sizeof(struct Test1)); printf("sizeof(Test2) = %d\n", sizeof(struct Test2)); return 0; }
相同的兩個struct,在調整了記憶體對齊方式後,Test1佔用的記憶體大小發生了變化。
3.3 struct記憶體對齊的規則:
- 第一個成員起始於
0偏移處
每個成員按照
對齊引數
進行對齊(型別大小
和pack引數
中較小的
一個)偏移地址
必須能被對齊引數
整除
(上一個成員的offset+size位置,偏移地址 / 對齊引數 == 0)結構體型別成員
的型別大小取其內部長度最大的資料
成員作為其大小
- struct的總長度必須為
所有對齊引數的整數倍
#include <stdio.h>
#pragma pack(8)
struct Test1
{
short a;
long b;
};
struct Test2
{
char c;
struct Test1 st;
double d;
};
#pragma pack()
int main()
{
printf("sizeof(Test1) = %d\n", sizeof(struct Test1));
printf("sizeof(Test2) = %d\n", sizeof(struct Test2));
return 0;
}
不同編譯器下的輸出:
vc:8 和 24
gcc:8 和 20
bcc:8 和 24
分析:vc和bcc的輸出結果符合我們的演算法,為什麼gcc不一樣的結果呢?
答案:#pragma
定義的很多編譯器指示字是特有的
,不同編譯器間不可移植
。gcc編譯器不支援8位元組對齊
,所以gcc編譯器看到'#pragma pack(8)'就直接刪掉、忽略了,仍然按照4位元組對齊
,得到結果為20:
struct Test2
{ pack offset memberSize
char c; 1 0 1
struct Test1 st; 4 4 8
double d; 4 12 8
} 總大小:20
本文總結自“狄泰軟體學院”唐佐林老師《C語言進階課程》。
如有錯漏之處,懇請指正。