VC和GCC默認(rèn)的都是4字節(jié)對(duì)齊,編程中可以使用#pragma pack(n)指定對(duì)齊模數(shù)。出現(xiàn)以上差異的原因在于,VC和GCC中對(duì)于double類型的對(duì)齊方式不同。
成都創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:網(wǎng)站設(shè)計(jì)制作、成都做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的孟連網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
Win32平臺(tái)下的微軟VC編譯器在默認(rèn)情況下采用如下的對(duì)齊規(guī)則: 任何基本數(shù)據(jù)類型T的對(duì)齊模數(shù)就是T的大小,即sizeof(T)。比如對(duì)于double類型(8字節(jié)),就要求該類型數(shù)據(jù)的地址總是8的倍數(shù),而char類型數(shù)據(jù)(1字節(jié))則可以從任何一個(gè)地址開始。
Linux下的GCC奉行的是另外一套規(guī)則:任何2字節(jié)大小(包括單字節(jié)嗎?)的數(shù)據(jù)類型(比如short)的對(duì)齊模數(shù)是2,而其它所有超過2字節(jié)的數(shù)據(jù)類型(比如long,double)都以4為對(duì)齊模數(shù)。
復(fù)雜類型(如結(jié)構(gòu))的默認(rèn)對(duì)齊方式是它最長(zhǎng)的成員的對(duì)齊方式,這樣在成員是復(fù)雜類型時(shí),可以最小化長(zhǎng)度。
struct{char a;double b;}
在VC中,因?yàn)榻Y(jié)構(gòu)中存在double和char,按照最長(zhǎng)數(shù)據(jù)類型對(duì)齊,char只占1B,但是加上后面的double所占空間超過8B,所以char獨(dú)占8B;而double占8B,一共16Byte。
在GCC中,double長(zhǎng)度超過4字節(jié),按照4字節(jié)對(duì)齊,原理同上,不過char占4字節(jié),double占兩個(gè)4字節(jié),一共12Byte。
現(xiàn)象1: 提示gas gld 比識(shí)別
措施: gnu編譯器發(fā)展到后來,越來越流行,更多使用別名為 as ld gcc等.
現(xiàn)象2: 提示字節(jié)對(duì)齊需要是 2的倍數(shù)
措施: 具體解決方法: 利用命令 sed -i 's/align 2/align 4/g' filename 替換align 2 為 align 4(align 3 替換為 align 8)
sed -i 's/align 2/align 4/g' boot/head.s
sed -i 's/align 3/align 8/g' boot/head.s
現(xiàn)象3: -fcombine-regs -mstring-insns選項(xiàng)不識(shí)別
措施: 此兩個(gè)選項(xiàng)已經(jīng)過時(shí),直接去掉即可
現(xiàn)象4: warning 特別多
措施: 將-Wall 替換為 -w
現(xiàn)象5: __stack_chk_fail 未定義
措施: 去網(wǎng)上搜了一下,在Makefile中的$(CFLAGS)后面加上-fno-stack-protector,即不需要棧保護(hù)
現(xiàn)象6: main.c 中_syscall0重復(fù)定義
措施: main.c static inline _syscall0(int, fork) 去掉static即可
現(xiàn)象7: 提示內(nèi)嵌匯編不符合語法限制
措施: 類似的問題在后面編譯中出現(xiàn)好多,C內(nèi)嵌匯編的格式 asm (匯編語句:輸入寄存器:輸出寄存器:可能被修改的寄存器),最新的GCC規(guī)定 輸入或輸出寄存器不能出現(xiàn)在可能被修改的寄存器中,目前看到網(wǎng)上的方法是把所有類似問題的可能被修改的寄存器全部刪掉 解決方案:find -type f -exec sed -i 's/:"\w{2}"(,"\w{2}") )/:) /g' {} ; 其中's/:"\w{2}"(,"\w{2}") /:/g'
現(xiàn)象8: 在 control.c 中清楚定義了 static unsigned char attr = 0x70 ,而在鏈接 control.o 時(shí),卻爆出 attr未定義。
措施: 用 nm -C control.o 查看其符號(hào),發(fā)現(xiàn)attr確實(shí)處于未定義狀態(tài)。故單獨(dú)編譯一個(gè)小程序定義靜態(tài)變量,查看其 .o 文件中,發(fā)現(xiàn)靜態(tài)變量定義正常。故考慮為編譯選項(xiàng)差異導(dǎo)致,最終發(fā)現(xiàn)因?yàn)?-O 編譯優(yōu)化選項(xiàng)導(dǎo)致,目前處理方式是去掉該選項(xiàng)。
現(xiàn)象9: build.c:(.text+0xde): undefined reference to `MAJOR'
措施: 通過分析編譯打印信息,發(fā)現(xiàn)編譯時(shí)沒有加入頭文件路徑 -Iinclude
現(xiàn)象10: fs/fs.o: In function check_disk_change':(.text+0x1b2f): undefined reference to invalidate_buffers'
措施: 查找發(fā)現(xiàn)此函數(shù)定義在buffer.c 中,且為內(nèi)聯(lián)函數(shù), 故嘗試將其更改為普通函數(shù), 然后編譯通過.
現(xiàn)象11: 編譯 build.c 時(shí)報(bào)錯(cuò):/usr/include/i386-linux -gnu/bits/stdio2.h:57:8: error: unknown type name ‘__gnuc_va_list’
措施: 分析發(fā)現(xiàn)時(shí)此系列錯(cuò)誤均由 -Iinclude 選項(xiàng)導(dǎo)致, 而該選項(xiàng)在 想象9 中加入, 故考慮去掉該選項(xiàng), 直接在build.c 中加入 MAJOR 宏定義.
#pragma pack(n) n的取值可以為1、2、4、8、16,在編譯過程中按照n個(gè)字結(jié)對(duì)齊 沒聽說過有n=3的情況...
16的話,大概是因?yàn)閘z的gcc默認(rèn)是64位的編譯吧,如果是64位的話,可以看一下int的字節(jié)數(shù),應(yīng)該是8吧,那當(dāng)然是以8為模了 我的是32位的,所以對(duì)齊模數(shù)默認(rèn)是4,12沒有問題
1.平臺(tái)原因:不是所有的硬件平臺(tái)都能訪問任意地址上的任何數(shù)據(jù);某些硬件平臺(tái)智能在某些地址處取特定類型的數(shù)據(jù),否則拋出硬件異常。
2.性能原因:數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能在自然邊界上對(duì)齊,原因在于,為了訪問未對(duì)齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問,而對(duì)齊的內(nèi)存訪問僅需要一次訪問。
規(guī)則:
數(shù)據(jù)成員對(duì)齊規(guī)則:結(jié)構(gòu)(struct)的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方,以后每個(gè)數(shù)據(jù)成員的對(duì)齊按照#pragma pack指定的數(shù)值和這個(gè)數(shù)據(jù)成員自身長(zhǎng)度中,比較小的那個(gè)進(jìn)行。
結(jié)構(gòu)(struct)的整體對(duì)齊規(guī)則:自數(shù)據(jù)成員完成各自對(duì)齊后,結(jié)構(gòu)本身也要對(duì)齊,對(duì)齊將按照#pragma pack指定的數(shù)值和結(jié)構(gòu)最大數(shù)據(jù)成員長(zhǎng)度中,比較小的那個(gè)進(jìn)行
結(jié)構(gòu)體作為成員:如果一個(gè)結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲(chǔ)
假設(shè)CPU要讀取一個(gè)4字節(jié)大小的數(shù)據(jù)到寄存器中(假設(shè)內(nèi)存讀取粒度是4)
1.數(shù)據(jù)從0字節(jié)開始(內(nèi)存對(duì)齊)
2.數(shù)據(jù)從1開始(內(nèi)存不對(duì)齊)
當(dāng)數(shù)據(jù)從0字節(jié)開始的時(shí)候,直接將0-3四個(gè)字節(jié)完全讀取到寄存器,結(jié)算完成
當(dāng)數(shù)據(jù)從1開始的時(shí)候,問題很復(fù)雜,首先將前4個(gè)字節(jié)讀到寄存器,再次讀取4-7字節(jié)的數(shù)據(jù)進(jìn)寄存器,接著把0字節(jié),567字節(jié)的數(shù)據(jù)剔除,最后合并1234字節(jié)的數(shù)據(jù)進(jìn)寄存器,對(duì)一個(gè)內(nèi)存未對(duì)齊的寄存器進(jìn)行了這么多額外操作,大大降低了CPU的性能。
這還屬于樂觀情況(性能原因),還有平臺(tái)的移植原因,因?yàn)橹挥胁糠諧PU肯干,其他部分CPU遇到未對(duì)齊邊界就直接罷工了
字節(jié)對(duì)齊:
1.第一個(gè)成員在與結(jié)構(gòu)體變量偏移量(offset)為0的地址處。
2.其他成員變量要對(duì)齊到對(duì)齊數(shù)的整數(shù)倍的地址處
(1)對(duì)齊數(shù)=對(duì)齊系數(shù)與該成員大小的較小值。
(2)如果有宏定義 #pragma pack(n); 則它中的n 就是對(duì)齊系數(shù)。
(3)VS中默認(rèn)的對(duì)齊系數(shù)為8,linux中的默認(rèn)為4.
3.結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)(每個(gè)成員變量除了第一個(gè)都有對(duì)齊數(shù))的整數(shù)倍。
4.如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對(duì)齊到自己的最大對(duì)齊數(shù)的整數(shù)倍處,
結(jié)構(gòu)體的整體大小就是所有最大對(duì)齊數(shù)(含嵌套結(jié)構(gòu)體的對(duì)齊數(shù))的整數(shù)倍。