C語(yǔ)言 詳細(xì)講解#pragma的使用方法
一、#pragma 簡(jiǎn)介
#pragma 用于指示編譯器完成一些特定的動(dòng)作
#pragma 所定義的很多指示字是編譯器特有的
#pragma 在不同的編譯器間是不可移植的
- 預(yù)處理器將忽略它不認(rèn)識(shí)的 #pragma 指令
- 不同的編譯器可能以不同的方式解釋同一條 #pragma 指令
一般用法:
#pragma parameter
注:不同的 parameter 參數(shù)語(yǔ)法和意義各不相同
二、#pragma message
- message 參數(shù)在大多數(shù)的編譯器中都有相似的實(shí)現(xiàn)
- message 參數(shù)在編譯時(shí)輸出消息到編譯輸出窗口中
- message 用于條件編譯中可提示代碼的版本信息
例如:
#if defined(ANDROID20) #pragma message("Compile Android SDK 2.0...") #define VERSION "Android 2.0 " #endif
注:#error 和 #warning 不同,#pragma message 僅僅代表一條編譯消息,不代表程序錯(cuò)誤。
下面看一個(gè) #pragma message 使用示例:
test.c:
#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; }
下面為輸出結(jié)果:
三、#pragma once
- #pragma once 用于保證頭文件只被編譯一次
- #pragma once 是編譯器相關(guān)的,不一定被支持
下面兩種方式的區(qū)別是:前者是被 C 語(yǔ)言所支持的,并不是只包含一次頭文件,而是包含多次,然后通過(guò)宏控制是否嵌入到源代碼中,也就是說(shuō)通過(guò)宏的方式,可以保證頭文件里面的內(nèi)容只被嵌入一次,但是由于包含了多次,預(yù)處理器還是處理了多次,所以效率上來(lái)說(shuō)比較低;后者是告訴預(yù)處理器當(dāng)前文件只編譯一次,所以說(shuō)效率較高。
下面看一個(gè) #pragma once 的使用示例:
global.h:
#pragma once int g_value = 1;
test.c:
#include <stdio.h> #include "global.h" #include "global.h" int main() { printf("g_value = %d\n", g_value); return 0; }
下面為輸出結(jié)果,可以看到雖然在test.c 定義了兩次global.h,但是程序編譯沒(méi)有報(bào)錯(cuò),且能正常運(yùn)行,這就是 #pragma once 作用的結(jié)果:
工程應(yīng)用中既想要移植性,又想要保證效率,可以采用以下做法:
global.h:
#ifndef _GLOBAL_H_ #define _GLOBAL_H_ #pragma once int g_value = 1; #endif
四、#pragma pack
什么是內(nèi)存對(duì)齊?
- 不同類型的數(shù)據(jù)在內(nèi)存中按照一定的規(guī)則排列
- 而不一定是順序的一個(gè)接一個(gè)的排列
下面想想 Test1 和 Test2 所占的內(nèi)存空間是否相同?
答案是否定的,Test1 占用 12 個(gè)字節(jié),而 Test 占用 8 個(gè)字節(jié)
為什么需要內(nèi)存對(duì)齊?
- CPU對(duì)內(nèi)存的讀取不是連續(xù)的,而是分成塊讀取的,塊的大小只能是1、2、4、8、16...字節(jié)
- 當(dāng)讀取操作的數(shù)據(jù)未對(duì)齊,則需要兩次總線周期來(lái)訪問(wèn)內(nèi)存,因此性能會(huì)大打折扣
- 某些硬件平臺(tái)只能從規(guī)定的相對(duì)地址處讀取特定類型的數(shù)據(jù),否則產(chǎn)生硬件異常
#pragma pack 用于指定內(nèi)存對(duì)齊方式
#pragma pack 能夠改變編譯器的默認(rèn)對(duì)齊方式
例如,下面代碼中,Test1 和 Test2 的內(nèi)存均為 8 個(gè)字節(jié)
struct 占用的內(nèi)存大小
第一個(gè)成員起始于 0 偏移處
每個(gè)成員按其類型大小和 pack 參數(shù)中較小的一個(gè)進(jìn)行對(duì)齊
- 偏移地址必須能被對(duì)齊參數(shù)整除
- 結(jié)構(gòu)體成員的大小取其內(nèi)部長(zhǎng)度最大的數(shù)據(jù)成員作為其大小
結(jié)構(gòu)體總長(zhǎng)度必須為所有對(duì)齊參數(shù)的整數(shù)倍
編譯器在默認(rèn)情況下按照 4 字節(jié)對(duì)齊。
下面通過(guò)代碼,手工計(jì)算結(jié)構(gòu)體所占用的內(nèi)存大小
test.c:
#include <stdio.h> #pragma pack(4) struct Test1 { //對(duì)齊參數(shù) 偏移地址 大小 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 { //對(duì)齊參數(shù) 偏移地址 大小 char c1; //1 0 1 char c2; //1 1 1 short s; //2 2 2 int i; //4 4 4 }; #pragma pack() int main() { printf("sizeof(Test1) = %d\n", sizeof(struct Test1)); printf("sizeof(Test2) = %d\n", sizeof(struct Test2)); return 0; }
以 Test1 為例,c1 類型大小為 1 字節(jié),而 pack 參數(shù)默認(rèn)為 4,所以對(duì)齊參數(shù)取最小為 1;同理 s 的對(duì)齊參數(shù)為 2,偏移地址要被 2 整除,所以為 2;同理 c 的對(duì)齊參數(shù)為 1,偏移地址為 4;同理,i 的對(duì)齊參數(shù)為 4,因?yàn)槠频刂芬鼙?對(duì)齊參數(shù) 4 整除,在偏移地址 4 之后能被 4 整除的最小偏移地址為 8,所以 i 的偏移地址為 8,而 i 占用 4 個(gè)字節(jié),所以 Test1 結(jié)構(gòu)體占用的總字節(jié)數(shù)為 12 字節(jié),這就和上面的圖對(duì)應(yīng)起來(lái)了。
下面再通過(guò)一個(gè)例子感受一下:
在 gcc 編譯器下,test.c:
#include <stdio.h> #pragma pack(8) struct S1 { //對(duì)齊參數(shù) 偏移地址 大小 short a; //2 0 2 long b; //4 4 4 }; struct S2 { //對(duì)齊參數(shù) 偏移地址 大小 char c; //1 0 1 struct S1 d; //4 4 8 double e; //4 12 8 }; #pragma pack() int main() { printf("%d\n", sizeof(struct S1)); printf("%d\n", sizeof(struct S2)); return 0; }
下面為輸出結(jié)果:
這里注意兩點(diǎn):
1.結(jié)構(gòu)體成員的大小取其內(nèi)部長(zhǎng)度最大的數(shù)據(jù)成員作為其大小,所以 S1 的內(nèi)存大小為 4,而pack 參數(shù)默認(rèn)為 8,所以對(duì)齊參數(shù)為 4
2.一般的 pack 對(duì)齊格式分別是 1,2,4,8,16,默認(rèn)的對(duì)齊格式,也就是:#pragmapack() 的情況下,會(huì)在結(jié)構(gòu)體中挑選占用字節(jié)最多的類型,例如 double 占用 8 個(gè)字節(jié)
3.gcc 編譯器暫時(shí)不支持 8 字節(jié)對(duì)齊,默認(rèn)按照 4 字節(jié)對(duì)齊,所以 S2 中的 e 的對(duì)齊參數(shù)為 4,故 S2 占用內(nèi)存大小為 20 字節(jié)。(學(xué)習(xí)采用的為 ubuntu 10.10)
如果把代碼放在 VS2012 里運(yùn)行,那結(jié)果就是和分析的一樣,S2 占用 24 字節(jié)。
#include <stdio.h> #pragma pack(8) struct S1 { //對(duì)齊參數(shù) 偏移地址 大小 short a; //2 0 2 long b; //4 4 4 }; struct S2 { //對(duì)齊參數(shù) 偏移地址 大小 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; }
結(jié)果:
五、小結(jié)
#pragma 用于指示編譯器完成一些特定的動(dòng)作
#pragma 所定義的很多指示字是編譯器特有的
- #pragma message 用于自定義編譯消息
- #pragma once 用于保證頭文件只被編譯一次
- #pragma pack 用于指定內(nèi)存對(duì)齊方式
到此這篇關(guān)于C語(yǔ)言 詳細(xì)講解#pragma的使用方法的文章就介紹到這了,更多相關(guān)C語(yǔ)言 #pragma內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++命名空間?缺省參數(shù)?const總結(jié)?引用總結(jié)?內(nèi)聯(lián)函數(shù)?auto關(guān)鍵字詳解
這篇文章主要介紹了C++命名空間?缺省參數(shù)?const總結(jié)?引用總結(jié)?內(nèi)聯(lián)函數(shù)?auto關(guān)鍵字詳解的相關(guān)資料,需要的朋友可以參考下2023-01-01C++實(shí)現(xiàn)有向圖鄰接表的構(gòu)建
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)有向圖鄰接表的構(gòu)建,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04使用C語(yǔ)言實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng)
這篇文章主要介紹了使用C語(yǔ)言實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09C++調(diào)用matlab引擎實(shí)現(xiàn)三維圖的繪制
這篇文章主要為大家詳細(xì)介紹了C++如何調(diào)用matlab引擎實(shí)現(xiàn)三維圖的繪制,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C++和Matlab有一定的幫助,需要的可以參考一下2022-12-12Qt數(shù)據(jù)庫(kù)應(yīng)用之實(shí)現(xiàn)文件編碼格式識(shí)別
在做數(shù)據(jù)導(dǎo)入導(dǎo)出的過(guò)程中,如果應(yīng)用場(chǎng)景多了,相信各位都會(huì)遇到一個(gè)問(wèn)題就是文件編碼的問(wèn)題。本文將用Qt實(shí)現(xiàn)文件編碼格式識(shí)別,感興趣的可以了解一下2022-06-06C語(yǔ)言驅(qū)動(dòng)開(kāi)發(fā)之內(nèi)核使用IO/DPC定時(shí)器詳解
本章將繼續(xù)探索驅(qū)動(dòng)開(kāi)發(fā)中的基礎(chǔ)部分,定時(shí)器在內(nèi)核中同樣很常用,在內(nèi)核中定時(shí)器可以使用兩種,即IO定時(shí)器,以及DPC定時(shí)器,感興趣的可以了解一下2023-04-04