堆路幸、棧與大小端存儲(chǔ)
前言
首先先提一個(gè)和操作系統(tǒng)主引導(dǎo)相關(guān)的概念:一個(gè)有效的主引導(dǎo)扇區(qū),其起始地址為0X7c00付翁,最后兩個(gè)自己的數(shù)據(jù)必須是0x55简肴、0xaa;否則這個(gè)扇區(qū)里保存的就不是一些有意而為的數(shù)據(jù)胆敞。這三個(gè)數(shù)都是所謂的"魔數(shù)"着帽,而0x7c00的由來(lái)是這樣的:雖然一個(gè)Mbr大小為512字節(jié),但是加上堆棧區(qū)大小應(yīng)該為1KB左右移层。而在8086CPU中0x0~0x3ff
是存放中斷向量表的仍翰,按照DOS1.0的32KB標(biāo)準(zhǔn),我們最好將其放置在末尾處观话,大小占1KB予借,所以起始位置為31KB處,即0x7c00.然后再看0x55與0xaa频蛔,如果是兩個(gè)單獨(dú)的字節(jié)灵迫,那么低地址放0x55,高地址放0xaa晦溪;但是如果是一個(gè)字單位瀑粥,呢就應(yīng)該是0xaa55
,這里涉及到了大小端字節(jié)序三圆。
但是自己突然又有個(gè)問(wèn)題狞换,這里的存放順序是大小端問(wèn)題避咆,但是數(shù)據(jù)存儲(chǔ)又分為存在堆與棧中,這兩者是怎么聯(lián)系起來(lái)的修噪?自己以前寫過(guò)關(guān)于結(jié)構(gòu)體內(nèi)存存放的一篇文章查库,現(xiàn)在看來(lái)當(dāng)時(shí)還沒(méi)有想到與堆棧之間的聯(lián)系,那現(xiàn)在來(lái)看一下數(shù)據(jù)存放與堆棧之間的關(guān)系黄琼。
大小端存儲(chǔ)
這個(gè)概念在網(wǎng)絡(luò)編程中應(yīng)該會(huì)涉及到樊销,雖然現(xiàn)在各種服務(wù)器模型和框架(事實(shí)上在設(shè)計(jì)socket網(wǎng)絡(luò)編程之初就已經(jīng)考慮了這個(gè)問(wèn)題)都已經(jīng)有了非常好的處理大小端存儲(chǔ)的方式,htonl脏款、ntohl....
围苫;但是了解一下為什么這么做也是比較重要的:
簡(jiǎn)單來(lái)說(shuō),從低地址往高地址(一般visual studio提供的內(nèi)存查看方式都是這樣弛矛,我們就采用這種描述方式)來(lái)看够吩,小端會(huì)將數(shù)據(jù)的低字節(jié)部分先放入,然后放高字節(jié)部分丈氓,就比如int x=0x12345678
,存放在內(nèi)存中就是78 56 34 12
强法。大端則和我們讀寫方式一致(從低到高地址的讀寫方式)万俗,同樣的x存放順序?yàn)?code>12 34 56 78.
堆與棧
首先看一下堆與棧的結(jié)構(gòu):
堆與棧其實(shí)是在同一段地址空間的,不同的是棧是往下增長(zhǎng)饮怯,堆是往上增長(zhǎng)闰歪;所以說(shuō)應(yīng)該寫道堆的數(shù)據(jù)因?yàn)樘鄬懙綏V惺呛侠淼?雖然不應(yīng)該這么做),但是因?yàn)闂J浅绦蜃詣?dòng)分配蓖墅、堆是自己分配的库倘,所以非常有可能會(huì)發(fā)生堆棧沖突!
在C++中论矾,內(nèi)存分成5個(gè)區(qū)教翩,分別是堆、棧贪壳、自由存儲(chǔ)區(qū)饱亿、全局/靜態(tài)存儲(chǔ)區(qū)和常量存儲(chǔ)區(qū)。
棧:就是那些由編譯器在需要的時(shí)候分配闰靴,在不需要的時(shí)候自動(dòng)清楚的變量的存儲(chǔ)區(qū)彪笼。里面的變量通常是局部變量、函數(shù)參數(shù)等蚂且。
堆:就是那些由new分配的內(nèi)存塊配猫,他們的釋放編譯器不去管,由我們的應(yīng)用程序去控制杏死,一般一個(gè)new就要對(duì)應(yīng)一個(gè)delete泵肄。如果程序員沒(méi)有釋放掉捆交,那么在程序結(jié)束后,操作系統(tǒng)會(huì)自動(dòng)回收凡伊。
自由存儲(chǔ)區(qū):就是那些由malloc等分配的內(nèi)存塊零渐,他和堆是十分相似的,不過(guò)它是用free來(lái)結(jié)束自己的生命的系忙。
全局/靜態(tài)存儲(chǔ)區(qū):全局變量和靜態(tài)變量被分配到同一塊內(nèi)存中诵盼,在以前的C語(yǔ)言中,全局變量又分為初始化的和未初始化的银还,在C++里面沒(méi)有這個(gè)區(qū)分了风宁,他們共同占用同一塊內(nèi)存區(qū)。
常量存儲(chǔ)區(qū):這是一塊比較特殊的存儲(chǔ)區(qū)蛹疯,他們里面存放的是常量戒财,不允許修改。
對(duì)于堆區(qū)捺弦、棧區(qū)和靜態(tài)存儲(chǔ)區(qū)它們之間最大的不同在于饮寞,棧的生命周期很短暫。但是堆區(qū)和靜態(tài)存儲(chǔ)區(qū)的生命周期相當(dāng)于與程序的生命同時(shí)存在(如果不在程序運(yùn)行中間將堆內(nèi)存delete的話)列吼,我們將這種變量或數(shù)據(jù)成為全局變量或數(shù)據(jù)幽崩。但是,對(duì)于堆區(qū)的內(nèi)存空間使用更加靈活寞钥,因?yàn)樗试S你在不需要它的時(shí)候慌申,隨時(shí)將它釋放掉,而靜態(tài)存儲(chǔ)區(qū)將一直存在于程序的整個(gè)生命周期中理郑。
聯(lián)系
我們分別寫一個(gè)非常簡(jiǎn)單的程序來(lái)看一下0x12345678
與a
這兩個(gè)是怎么存儲(chǔ)在內(nèi)存中的:
棧中:
堆中:
靜態(tài)存儲(chǔ)區(qū)中:
但是自己一直覺(jué)得有問(wèn)題的地方是這樣的蹄溉,我們都知道程序在“中斷”跳轉(zhuǎn)的時(shí)候是會(huì)在棧中保存程序的上下文的(返回地址、變量.....)您炉;所以我認(rèn)為我們?cè)诒4娼Y(jié)構(gòu)體的時(shí)候柒爵,雖然結(jié)構(gòu)體也是在編譯的時(shí)候視為數(shù)據(jù)類型,然后分配在棧中邻吭。但是結(jié)構(gòu)體中的成員變量的存放應(yīng)該也和自身的類型有關(guān)餐弱,所以如果他們也是局部變量的時(shí)候應(yīng)該也分配在棧中,其地址順序也應(yīng)該符合棧的存放方式囱晴,但是看下圖:
順序不對(duì)案囹尽?其實(shí)這里犯了一個(gè)很大的錯(cuò)誤畸写,就是并沒(méi)有意識(shí)到成員變量此時(shí)并不是所謂的"局部變量"了M郧啤!而是非靜態(tài)成員變量枯芬,我們可以通過(guò)類的內(nèi)存分配看到如下結(jié)構(gòu):
這里的x與c都是非靜態(tài)成員變量论笔,是分配在堆中的采郎,所以可以解釋了。
而成員變量和局部變量是有區(qū)別的:
a定義的位置不同
成員變量:定義于類中狂魔,作用于整個(gè)類
局部變量:定義于方法或者語(yǔ)句中蒜埋,作用于該方法或者該語(yǔ)句。
b內(nèi)存中出現(xiàn)的時(shí)間和位置不同
成員變量:當(dāng)對(duì)象創(chuàng)建時(shí)最楷,出現(xiàn)在堆內(nèi)存當(dāng)中整份。
局部變量:所屬區(qū)域被運(yùn)算時(shí),出現(xiàn)在棧內(nèi)存當(dāng)中籽孙。
c生命周期不同
成員變量:隨著對(duì)象的出現(xiàn)而出現(xiàn)烈评,隨著對(duì)象的消失而消失。
局部變量:隨著所屬區(qū)域運(yùn)算結(jié)束犯建,它就被釋放讲冠。
d初始化值不同
成員變量:成員變量因?yàn)樵诙褍?nèi)存中,所以它有默認(rèn)初始值适瓦。
局部變量:沒(méi)有默認(rèn)的初始值竿开。
那么如果我們?cè)赾lass中定義一個(gè)函數(shù),函數(shù)中用了局部變量玻熙,那么這個(gè)類中非靜態(tài)成員函數(shù)的局部變量是符合我們說(shuō)的數(shù)據(jù)存放規(guī)則的么德迹?答案是:是的!
這里引用一下總結(jié)的靜態(tài)成員變量與非靜態(tài)成員變量的區(qū)別:
1揭芍、從保存位置:
a) 靜態(tài)成員變量: 方法區(qū)的靜態(tài)區(qū)域
b) 非靜態(tài)成員變量: 堆內(nèi)存中的對(duì)象空間里面
2、從書寫格式上看:
a) 靜態(tài)成員變量: 在數(shù)據(jù)類型前面多了一個(gè)static修飾
b) 非靜態(tài)成員變量: 沒(méi)有static修飾
3卸例、從生命周期上看:
a) 靜態(tài)成員變量: 在類加載的時(shí)候称杨,類加載完成,就分配完空間筷转;直到類被卸載時(shí)空間被回收
b) 非靜態(tài)成員變量: 創(chuàng)建對(duì)象的時(shí)候分配空間姑原; 對(duì)象變?yōu)槔臻g被回收的時(shí)候被銷毀
4、從使用方法上看:
a) 靜態(tài)成員變量: 直接通過(guò)類名使用
b) 非靜態(tài)成員變量: 必須通過(guò)對(duì)象使用
5呜舒、從修改后的影響范圍上看:
a) 靜態(tài)成員變量: 對(duì)該類的所有對(duì)象都有影響锭汛;
b) 非靜態(tài)成員變量: 只對(duì)一個(gè)對(duì)象有影響
結(jié)語(yǔ)
其實(shí)寫完以后發(fā)現(xiàn)其實(shí)并不需要糾結(jié)的,就是根據(jù)變量類型按照堆或棧的順序存儲(chǔ)袭蝗,然后再根據(jù)數(shù)據(jù)大小分成字節(jié)唤殴,再按字節(jié)序存儲(chǔ)就行了。