【C语言进阶深度学习记录】十九 #pragma使用与分析
文章目錄
- 1 #pragma 概念簡(jiǎn)介
- 1.1 #pragma message 的用法
- 1.2 #pragma once 的用法
- 1.3 #pragma pack 的用法
- 1.31 struct占用的內(nèi)存大小如何計(jì)算
- 2 總結(jié)
在學(xué)習(xí) #pragma 之前 ,我們首先要明白一點(diǎn), #pragma 的實(shí)現(xiàn),在不同的編譯器之間是不同的,所以使用它的代碼,基本上不能移植代碼。但是它也有它自己的用處,還是要學(xué)習(xí)以下。
1 #pragma 概念簡(jiǎn)介
- #pragma 是唯一一個(gè)預(yù)處理器不處理的指令,它需要保留給編譯器處理。
- #pragma 用于指示編譯器完成一些特殊的動(dòng)作 (看后面就知道什么意思了)
- #pragma 所定義的很多指示字,是編譯器特有的
- #pragma 在不同的編譯器之間是不能移植的
因?yàn)椴煌木幾g器的 #pragma 的實(shí)現(xiàn)是不同的。所以:
一般用法為:
注意:不同的 parameter 參數(shù)的語(yǔ)法和意義各不相同
1.1 #pragma message 的用法
- message的參數(shù)在大多數(shù)的編譯器中都有相似的實(shí)現(xiàn)
- message參數(shù)在編譯時(shí),輸出消息到編譯輸出窗口中。注意是在編譯時(shí),不是程序運(yùn)行時(shí)。
- 如果將message用于條件編譯中,可以提示代碼的版本信息。如下圖所示:
如下代碼是 #pragma message的使用分析
- 24-1-lyy.c
上面的 #error 的意思是如果沒(méi)有定義上述的宏,就會(huì)將這句話在編譯的時(shí)候打印出來(lái),代表我們想要了解的錯(cuò)誤。
- 上述代碼如果這樣編譯,不定義宏:gcc 24-1-lyy.c -o 24-1-lyy.out 在編譯時(shí)將顯示錯(cuò)誤如下:
- 如果這樣編譯,在命令行中定義宏 ANDROID23 ,gcc -DANDROID23 24-1-lyy.c -o 24-1-lyy.out 將在編譯時(shí)顯示如下信息:
注意一點(diǎn),上述的信息是在編譯的時(shí)候打印輸出的,不是在程序運(yùn)行的時(shí)候輸出的。
1.2 #pragma once 的用法
首先說(shuō)一下它的作用:
- #pragma once 用于保證頭文件只被編譯一次。也就是可以避免重復(fù)包含頭文件。這與上一篇文章使用條件編譯避免重復(fù)包含頭文件的作用是一樣的:【C語(yǔ)言進(jìn)階深度學(xué)習(xí)記錄】十八 條件編譯的使用與分析
- #pragma once 是編譯器相關(guān)的,不一定被支持。
那么 #pragma once 與之前學(xué)的條件編譯來(lái)避免重復(fù)包含頭文件,這兩種方式有什么區(qū)別呢?
- #pragma once效率會(huì)更高,因?yàn)樗槐WC被編譯一次,不會(huì)去判斷是否定定義了相關(guān)宏。所以效率更高。
下面的代碼:
- 24-2.c
- global.h
- 對(duì)上述代碼進(jìn)行編譯運(yùn)行:gcc 24-2.c -o 24-2.out
可以看出,雖然上面的代碼包含了兩次global頭文件,但是編譯并沒(méi)有報(bào)錯(cuò)。因?yàn)樵陬^文件中使用了#pragma once ,使得該頭文件只能被編譯一次,作用與在頭文件中使用條件編譯指令一樣。
1.3 #pragma pack 的用法
什么是內(nèi)存對(duì)齊?
- 不同類(lèi)型的數(shù)據(jù)在內(nèi)存中按照一定的規(guī)則排列
- 但是不一定是順序的一個(gè)接一個(gè)的排列
例如下圖中的兩個(gè)結(jié)構(gòu)體的大小是不一樣的,因?yàn)樗鼈兊膬?nèi)存布局是不一樣的:
它們的內(nèi)存布局如下圖:
至于為什么是上圖這樣的對(duì)齊方式,下一篇文章會(huì)進(jìn)行學(xué)習(xí)。
先來(lái)加單的說(shuō)一下為什么需要內(nèi)存對(duì)齊:
- #pragma pack 用于指定內(nèi)存的對(duì)齊方式。用于修改編譯器的默認(rèn)對(duì)齊方式
一般來(lái)講,在Linux系統(tǒng)中,編譯器的默認(rèn)對(duì)我方式是4字節(jié)對(duì)齊。下圖中的代碼,可以將對(duì)齊方式修改為1字節(jié)對(duì)齊:
1.31 struct占用的內(nèi)存大小如何計(jì)算
對(duì)于不同的內(nèi)存對(duì)齊方式,上面的結(jié)構(gòu)體在內(nèi)存中的布局是不一樣的,那么我們?nèi)绾蝸?lái)計(jì)算不同的對(duì)齊方式在內(nèi)存中的布局是什么樣的呢?
需要根據(jù)以下三點(diǎn):
第一個(gè)struct成員永遠(yuǎn)起始于 0偏移處
每個(gè)成員按其類(lèi)型大小和pack參數(shù)中較小的一個(gè) 進(jìn)行對(duì)齊
2.1 偏移地址必須能被對(duì)齊參數(shù)整除
2.2 如果一個(gè)結(jié)構(gòu)體中有一個(gè)變量也是結(jié)構(gòu)體,那么這個(gè)內(nèi)部的結(jié)構(gòu)體成員的對(duì)齊大小就按照其內(nèi)部最大的數(shù)據(jù)成員作為其大小來(lái)計(jì)算
結(jié)構(gòu)體總長(zhǎng)度,必須為所有對(duì)齊參數(shù)的整數(shù)倍
- 只需要按照上述三點(diǎn)計(jì)算方法,就可以計(jì)算出所有結(jié)構(gòu)體的大小以及內(nèi)存布局的樣式
如下面的代碼:
- 24-3.c
- 編譯運(yùn)行結(jié)果為:
sizeof(Test1) = 10
sizeof(Test2) = 8
- 結(jié)果分析1–Test1:
對(duì)于結(jié)構(gòu)體Test1,2字節(jié)對(duì)齊,按照上述三個(gè)計(jì)算條件有:
struct Test1 { //對(duì)齊方式 大小 起始地址 占用的內(nèi)存地址位置 char c1; 2 大于 1 0 0 short s; 2 等于 2 2(被對(duì)齊參數(shù)2整除) 2~3 char c2; 2 大于 1(對(duì)齊參數(shù)) 4 4 int i; 2(對(duì)齊參數(shù)) 小于 4 6(被對(duì)齊參數(shù)2整除) 6~9 };然后根據(jù):
每個(gè)成員按其類(lèi)型大小和pack參數(shù)中較小的一個(gè) 進(jìn)行對(duì)齊。比如上面的起始地址計(jì)算那里,都是被對(duì)齊參數(shù)2整除,這個(gè)2是類(lèi)型大小和pack參數(shù)中較小的一個(gè)
2.1 偏移地址必須能被對(duì)齊參數(shù)整除
這一條規(guī)則計(jì)算每個(gè)成員的起始地址。如上面的計(jì)算。
- 結(jié)果分析2–Test2
對(duì)于結(jié)構(gòu)體Test2,4字節(jié)對(duì)齊,按照上述三個(gè)計(jì)算條件有:
struct Test2 { //對(duì)齊方式 大小 起始地址 占用的內(nèi)存地址位置 char c1; 4 大于 1(對(duì)齊參數(shù)) 0 0 char c2; 4 大于 1(對(duì)齊參數(shù)) 1(被對(duì)齊參數(shù)1整除) 1 short s; 4 大于 2(對(duì)齊參數(shù)) 2(被對(duì)齊參數(shù)2整除) 2~3 int i; 4 等于 4(對(duì)齊參數(shù)) 4(被對(duì)齊參數(shù)4整除) 5~7 };然后再根據(jù):
每個(gè)成員按其類(lèi)型大小和pack參數(shù)中較小的一個(gè) 進(jìn)行對(duì)齊。比如上面的起始地址計(jì)算那里,對(duì)齊參數(shù)分別為1,1,2,4
2.1 偏移地址必須能被對(duì)齊參數(shù)整除
這一條規(guī)則計(jì)算每個(gè)成員的起始地址。如上面的計(jì)算。
經(jīng)過(guò)上面的兩個(gè)結(jié)構(gòu)體大小的計(jì)算,可以很容易的畫(huà)出其內(nèi)存圖
再看一個(gè)復(fù)雜的結(jié)構(gòu)體大小的計(jì)算
- 代碼 24-4.c
運(yùn)行結(jié)果為:
sizeof(struct S1) = 8
sizeof(struct S2) = 20
S1很好分析。下面我們分析S2
struct S2 //對(duì)齊方式 大小 起始地址 占用的內(nèi)存地址位置 {char c; 4 大于 1(對(duì)齊參數(shù)) 0 0 struct S1 d; 4 等于 4(這個(gè)是S1中最大參數(shù)大小) 4(被4整除) 4~11(S1實(shí)際大小為8) double e; 4(對(duì)齊)小于 8 12(被4整除) 12~19 };然后再根據(jù):
每個(gè)成員按其類(lèi)型大小和pack參數(shù)中較小的一個(gè) 進(jìn)行對(duì)齊。比如上面的起始地址計(jì)算那里,對(duì)齊參數(shù)分別為0,4,12
2.1 偏移地址必須能被對(duì)齊參數(shù)整除
2.2 如果一個(gè)結(jié)構(gòu)體中有一個(gè)變量也是結(jié)構(gòu)體,那么這個(gè)內(nèi)部的結(jié)構(gòu)體成員的對(duì)齊大小就按照其內(nèi)部最大的數(shù)據(jù)成員作為其大小來(lái)計(jì)算
這一條規(guī)則計(jì)算每個(gè)成員的起始地址。如上面的計(jì)算。
我所使用的編譯器gcc 4.4.5 不支持8字節(jié)對(duì)齊方式
2 總結(jié)
- #pragma 用于指示編譯器完成以下特殊的動(dòng)作
- #pragma 對(duì)于不同的編譯器,可能底層實(shí)現(xiàn)原理不太一樣
- #pragma message 用于自定義編譯消息
- #pragma once 用于避免重復(fù)包含頭文件
- #pragma pack 用于指定內(nèi)存對(duì)齊方式
總結(jié)
以上是生活随笔為你收集整理的【C语言进阶深度学习记录】十九 #pragma使用与分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: idea 谷歌翻译
- 下一篇: xp下添加linux启动,如何在wind