C語(yǔ)言是面向過(guò)程的,而C++是面向?qū)ο蟮?/p>
C和C++的區(qū)別:
C是一個(gè)結(jié)構(gòu)化語(yǔ)言,它的重點(diǎn)在于算法和數(shù)據(jù)結(jié)構(gòu)。C程序的設(shè)計(jì)首要考慮的是如何通過(guò)一個(gè)過(guò)程,對(duì)輸入(或環(huán)境條件)進(jìn)行運(yùn)算處理得到輸出(或?qū)崿F(xiàn)過(guò)程(事務(wù))控制)。
C++,首要考慮的是如何構(gòu)造一個(gè)對(duì)象模型,讓這個(gè)模型能夠契合與之對(duì)應(yīng)的問(wèn)題域,這樣就可以通過(guò)獲取對(duì)象的狀態(tài)信息得到輸出或?qū)崿F(xiàn)過(guò)程(事務(wù))控制。 所以C與C++的最大區(qū)別在于它們的用于解決問(wèn)題的思想方法不一樣。之所以說(shuō)C++比C更先進(jìn),是因?yàn)椤?設(shè)計(jì)這個(gè)概念已經(jīng)被融入到C++之中 ”。

C與C++的最大區(qū)別:在于它們的用于解決問(wèn)題的思想方法不一樣。之所以說(shuō)C++比C更先進(jìn),是因?yàn)椤?設(shè)計(jì)這個(gè)概念已經(jīng)被融入到C++之中 ”,而就語(yǔ)言本身而言,在C中更多的是算法的概念。那么是不是C就不重要了,錯(cuò)!算法是程序設(shè)計(jì)的基礎(chǔ),好的設(shè)計(jì)如果沒(méi)有好的算法,一樣不行。而且,“C加上好的設(shè)計(jì)”也能寫(xiě)出非常好的東西。

什么是內(nèi)存對(duì)齊?看下面的結(jié)構(gòu)體

小編推薦一個(gè)學(xué)C語(yǔ)言/C++的學(xué)習(xí)裙【 六九九,四七零,五九六 】,無(wú)論你是大牛還是小白,是想轉(zhuǎn)行還是想入行都可以來(lái)了解一起進(jìn)步一起學(xué)習(xí)!裙內(nèi)有開(kāi)發(fā)工具,很多干貨和技術(shù)資料分享!
struct StudentTest{
char ch1;
short s;
char ch2;
int i;
};
假設(shè)這個(gè)結(jié)構(gòu)的成員在內(nèi)存中是緊湊排列的,
假設(shè)ch1的地址是0x00000000,那么s的地址就應(yīng)該是0x00000001,
ch2的地址應(yīng)該0x00000003,i的地址應(yīng)該是0x00000004。
實(shí)際情況如何,寫(xiě)一個(gè)小程序來(lái)測(cè)試下:
struct StudentTest{
char ch1;
short s;
char ch2;
int i;
};
int main(void)
{
struct StudentTest a;
printf("ch1 %p,s %p,ch2 %p,i %p ",
(unsigned int)(void*)&a.ch1-(unsigned int)(void*)&a,
(unsigned int)(void*)&a.s-(unsigned int)(void*)&a,
(unsigned int)(void*)&a.ch2-(unsigned int)(void*)&a,
(unsigned int)(void*)&a.i-(unsigned int)(void*)&a
);
return 0;
}
實(shí)際輸出如下:
小編推薦一個(gè)學(xué)C語(yǔ)言/C++的學(xué)習(xí)裙【 六九九,四七零,五九六 】,無(wú)論你是大牛還是小白,是想轉(zhuǎn)行還是想入行都可以來(lái)了解一起進(jìn)步一起學(xué)習(xí)!裙內(nèi)有開(kāi)發(fā)工具,很多干貨和技術(shù)資料分享!
這就是內(nèi)存對(duì)齊導(dǎo)致的問(wèn)題。
為什么會(huì)有內(nèi)存對(duì)齊?
字、雙字和四字在自然邊界上方是不需要對(duì)齊的,(對(duì)于字,雙字和四字來(lái)說(shuō)自然邊界分別是偶數(shù)地址,能被4整除的地址和能被8整除的地址)。為了提高程序的性能,數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能的在自然邊界上對(duì)齊。原因在于,為了訪問(wèn)未對(duì)齊的內(nèi)存,處理器需要做兩次內(nèi)存訪問(wèn)。而對(duì)齊的內(nèi)存只需做一次內(nèi)存訪問(wèn)。 一個(gè)字或雙字操作數(shù)跨越了4 字節(jié)邊界,或者一個(gè)四字操作數(shù)跨越了8 字節(jié)邊界,被認(rèn)為是未對(duì)齊的,從而需要兩次總線周期來(lái)訪問(wèn)內(nèi)存。一個(gè)字起始地址是奇數(shù)但卻沒(méi)有跨越字邊界被認(rèn)為是對(duì)齊的,能夠在一個(gè)總線周期中被訪問(wèn)。某些操作雙四字的指令需要內(nèi)存操作數(shù)在自然邊界上對(duì)齊。如果操作數(shù)沒(méi)有對(duì)齊,這些指令將會(huì)產(chǎn)生一個(gè)通用保護(hù)異常。雙四字的自然邊界是能夠被16 整除的地址。其他的操作雙四字的指令允許未對(duì)齊的訪問(wèn)(不會(huì)產(chǎn)生通用保護(hù)異常),然而,需要額外的內(nèi)存總線周期來(lái)訪問(wèn)內(nèi)存中未對(duì)齊的數(shù)據(jù)。
缺省情況下,編譯器默認(rèn)將結(jié)構(gòu)、棧中的成員數(shù)據(jù)進(jìn)行內(nèi)存對(duì)齊。因此,上面的程序輸出就變成了:
c1 00000000, s 00000002, c2 00000004, i 00000008。
編譯器將未對(duì)齊的成員向后移,將每一個(gè)都成員對(duì)齊到自然邊界上,從而也導(dǎo)致了整個(gè)結(jié)構(gòu)的尺寸變大。盡管會(huì)犧牲一點(diǎn)空間(成員之間有部分內(nèi)存空閑),但提高了性能。也正是這個(gè)原因,我們不可以斷言sizeof(TestStruct1)的結(jié)果為8。在這個(gè)例子中,sizeof(TestStruct1)的結(jié)果為12。
如何避免內(nèi)存對(duì)齊影響?
那么,能不能既達(dá)到提高性能的目的,又能節(jié)約一點(diǎn)空間呢?有一點(diǎn)小技巧可以使用。比如我們可以將上面的結(jié)構(gòu)改成:
struct StudentTest2
{
char c1;
char c2;
short s;
int i;
};
這樣一來(lái),每個(gè)成員都對(duì)齊在其自然邊界上,從而避免了編譯器自動(dòng)對(duì)齊。在這個(gè)例子中,sizeof(StudentTest2)的值為8。這個(gè)技巧有一個(gè)重要的作用,尤其是這個(gè)結(jié)構(gòu)作為API的一部分提供給第三方開(kāi)發(fā)使用的時(shí)候。第三方開(kāi)發(fā)者可能將編譯器的默認(rèn)對(duì)齊選項(xiàng)改變,從而造成這個(gè)結(jié)構(gòu)在你的發(fā)行的DLL 中使用某種對(duì)齊方式,而在第三方開(kāi)發(fā)者哪里卻使用另外一種對(duì)齊方式。這將會(huì)導(dǎo)致重大問(wèn)題。比如,StudentTest結(jié)構(gòu),我們的DLL 使用默認(rèn)對(duì)齊選項(xiàng),對(duì)齊為c1 00000000, s 00000002, c2 00000004, i 00000008,同時(shí)sizeof(StudentTest)的值為12。
而第三方將對(duì)齊選項(xiàng)關(guān)閉,導(dǎo)致
c1 00000000, s 00000001, c2 00000003, i 00000004,同時(shí)sizeof(StudentTest)的值為8。
除此之外我們還可以利用#pragma pack()來(lái)改變編譯器的默認(rèn)對(duì)齊方式。
使用指令#pragma pack (n),編譯器將按照n 個(gè)字節(jié)對(duì)齊。
使用指令#pragma pack (),編譯器將取消自定義字節(jié)對(duì)齊方式。
在#pragma pack (n)和#pragma pack ()之間的代碼按n 個(gè)字節(jié)對(duì)齊。
但是,成員對(duì)齊有一個(gè)重要的條件,即每個(gè)成員按自己的方式對(duì)齊.也就是說(shuō)雖然指定了按n 字節(jié)對(duì)齊,但并不是所有的成員都是以n 字節(jié)對(duì)齊。其對(duì)齊的規(guī)則是,每個(gè)成員按其類型的對(duì)齊參數(shù)(通常是這個(gè)類型的大小)和指定對(duì)齊參數(shù)(這里是n 字節(jié))中較小的一個(gè)對(duì)齊,即:
min( n, sizeof( item )) 。并且結(jié)構(gòu)的長(zhǎng)度必須為所用過(guò)的所有對(duì)齊參數(shù)的整數(shù)倍,不夠就補(bǔ)空字節(jié)??慈缦吕樱?/p>
#pragma pack(8)
struct TestStruct4{
char a;
long b;
};
struct TestStruct5{
char c;
TestStruct4 d;
long long e;
};
#pragma pack()
A),sizeof(TestStruct4) = ?
B), TestStruct5 的c 后面空了幾個(gè)字節(jié)接著是d?
TestStruct4 中,成員a 是1 字節(jié)默認(rèn)按1 字節(jié)對(duì)齊,指定對(duì)齊參數(shù)為8,這兩個(gè)值中取1,a按1 字節(jié)對(duì)齊;成員b 是4 個(gè)字節(jié),默認(rèn)是按4 字節(jié)對(duì)齊,這時(shí)就按4 字節(jié)對(duì)齊,所以sizeof(TestStruct4)應(yīng)該為8;
TestStruct5 中,c 和TestStruct4 中的a 一樣,按1 字節(jié)對(duì)齊,而d 是個(gè)結(jié)構(gòu),它是8 個(gè)字節(jié),它按什么對(duì)齊呢?對(duì)于結(jié)構(gòu)來(lái)說(shuō),它的默認(rèn)對(duì)齊方式就是它的所有成員使用的對(duì)齊參數(shù)中最大的一個(gè), TestStruct4 的就是4.所以,成員d 就是按4 字節(jié)對(duì)齊.成員e 是8 個(gè)字節(jié),它是默認(rèn)按8字節(jié)對(duì)齊,和指定的一樣,所以它對(duì)到8 字節(jié)的邊界上,這時(shí),已經(jīng)使用了12 個(gè)字節(jié)了,所以又添
加了4 個(gè)字節(jié)的空,從第16 個(gè)字節(jié)開(kāi)始放置成員e.這時(shí),長(zhǎng)度為24,已經(jīng)可以被8(成員e 按8字節(jié)對(duì)齊)整除.這樣,一共使用了24 個(gè)字節(jié).內(nèi)存布局如下(*表示空閑內(nèi)存,1 表示使用內(nèi)存。單位為1byte):
a b
TestStruct4 的內(nèi)存布局:1***, 1111,
c TestStruct4.a TestStruct4.b d
TestStruct5 的內(nèi)存布局: 1***, 1***, 1111, ****, 11111111
這里有三點(diǎn)很重要:
首先,每個(gè)成員分別按自己的方式對(duì)齊,并能最小化長(zhǎng)度。
其次,復(fù)雜類型(如結(jié)構(gòu))的默認(rèn)對(duì)齊方式是它最長(zhǎng)的成員的對(duì)齊方式,這樣在成員是復(fù)雜類型時(shí),可以最小化長(zhǎng)度。然后,對(duì)齊后的長(zhǎng)度必須是成員中最大的對(duì)齊參數(shù)的整數(shù)倍,這樣在處理數(shù)組時(shí)可以保證每一項(xiàng)都邊界對(duì)齊。
補(bǔ)充一下,對(duì)于數(shù)組,比如:char a[3];它的對(duì)齊方式和分別寫(xiě)3 個(gè)char 是一樣的.也就是說(shuō)
它還是按1 個(gè)字節(jié)對(duì)齊.如果寫(xiě): typedef char Array3[3];Array3 這種類型的對(duì)齊方式還是按1個(gè)字節(jié)對(duì)齊,而不是按它的長(zhǎng)度。但是不論類型是什么,對(duì)齊的邊界一定是1,2,4,8,16,32,64....中的一個(gè)。
另外,注意別的#pragma pack 的其他用法:
#pragma pack(push) //保存當(dāng)前對(duì)其方式到packing stack
#pragma pack(push,n) 等效于
#pragma pack(push)
#pragma pack(n) //n=1,2,4,8,16 保存當(dāng)前對(duì)齊方式,設(shè)置按n 字節(jié)對(duì)齊。
#pragma pack(pop) //packing stack 出棧,并將對(duì)其方式設(shè)置為出棧的對(duì)齊

小編推薦一個(gè)學(xué)C語(yǔ)言/C++的學(xué)習(xí)裙【 六九九,四七零,五九六 】,無(wú)論你是大牛還是小白,是想轉(zhuǎn)行還是想入行都可以來(lái)了解一起進(jìn)步一起學(xué)習(xí)!裙內(nèi)有開(kāi)發(fā)工具,很多干貨和技術(shù)資料分享!
這些是C/C++能做的
服務(wù)器開(kāi)發(fā)工程師、人工智能、云計(jì)算工程師、信息安全(黑客反黑客)、大數(shù)據(jù) 、數(shù)據(jù)平臺(tái)、嵌入式工程師、流媒體服務(wù)器、數(shù)據(jù)控解、圖像處理、音頻視頻開(kāi)發(fā)工程師、游戲服務(wù)器、分布式系統(tǒng)、游戲輔助等


