從記憶體角度深入看結構體(window/linux)大小
今天我們來看一下windows(32, 64)(dev-c++,vc),linux(32, 64)不同系統下,
它們求結構體大小時,編譯器到底給它們分配了哪些記憶體,又為什麼這樣分配,為啥子編譯器給它們有時空閒3個記憶體塊,有時候又空閒7個記憶體塊,為什麼啊,為什麼啊
當你們讀了上面的內容,還想繼續往下看的時候,就說明你開始關注記憶體的分配問題了,哈哈!!!
關於記憶體對齊:
簡單地理解就是:程式中,資料結構中的變數等等都需要佔用記憶體,系統採用記憶體對齊,就可以提高訪問速度(為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問,而經過記憶體對齊一次就可以了)
大家看下面這個程式:
#include <stdio.h>
struct stu{
char a;
int b;
char c;
};
int main(){
struct stu ss;
printf("%d\n", sizeof(ss));
printf("%p\n", &ss.a);
printf("%p\n", &ss.b);
printf("%p\n", &ss.c);
return 0;
}
當前結構體裡面的成員型別是:(char , int, char)
下面大家看程式執行結果:
結果一:
win7,32位,vc中
win7,64位,vc中
結果二:
win7,32位,dev-c++中
win7,64位,dev-c++中
我們知道這涉及一個概念——偏移量
網上給出的解釋是:它指的是結構體變數中成員的地址和結構體變數地址的差。
結構體大小的定義是:結構體的大小等於最後一個成員的偏移量加上最後一個成員的大小
哈哈,聽饒了吧!
其實,當前偏移量的大小就是編譯器為前面變數所分配的記憶體個數。
大家先看一下上面結構體在記憶體中(下面的是在dev中分配的)的情況
首先,我們知道編譯器給一些變數分配記憶體的時候,它是一個連續的塊,它是以一個4位元組簡單對齊的
如上圖所示:
編譯器首先分配了一塊地址也就是:0022FEB4給了結構體的首地址(也就是結構體中的成員a)因為它是一個字元,它佔用一個記憶體地址,第二個是int型別,它佔用4個位元組,第一組已經用掉一格,還剩3格,肯定無法放下int型,考慮到記憶體對齊,所以不得不把它放到第二個組塊(0022feb8),第三個型別是char型別,跟第一個類似,佔用一個儲存塊(0022febc),(又因為它是4位元組簡單對齊方式),
所以空閒的三個儲存塊也同樣分給了當前這個結構體。
好,我們接下來繼續分析:
看程式碼2:
#include <stdio.h>
struct stu{
char a;
char b;
int c;
};
int main(){
struct stu ss;
printf("%d\n", sizeof(ss));
printf("%p\n", &ss.a);
printf("%p\n", &ss.b);
printf("%p\n", &ss.c);
return 0;
}
哈哈,可別看錯了,我可跟上面的程式碼不一樣哎!!!!!
(結構體裡面定義的成員是: char, char , int)
好,下面大家看一下執行結果:
win7,32位,vc中
win7,64位,vc中
結果二:
win7,32位,dev-c++中
win7,64位,dev-c++中
來,不要急,我們接著看記憶體,分析分析它:
同樣的道理:當前結構體中,第一個型別是char型別,它佔用一個位元組(也就是0022FEB8),然後,接下來第二個型別還是char,佔用一個位元組,又因為上一組4個位元組(其中0022FEB9,0022FEBA, 0022FEBB均空著)還空著3個,可以放下第二個char,所以第二個char就放在了記憶體(0022FEB9)處,然後碰到int型別中,佔用4個位元組,上一組4個位元組中還剩下兩個空閒(其中0022FEBA, 0022FEBB),不夠放int型別,所以,新找了另一組連續的4個位元組。所以說:當前結構體總總共佔用8個位元組。
哈哈,所以說:結構體中,編譯器為所分配的所有空閒記憶體(滿足4位元組簡單對齊)都算在內,總的結構體大小也就相應的出來了。
簡單的說:結構體的成員變數型別(如1:char, int , char 如2:char, char, int),它們成員一樣,但順序不一樣,編譯器給分配的記憶體也不一樣,我們應儘可能的使其記憶體佔用的少一點,這樣,總的來說對我們的程式執行只有好處。
這回大家明白的差不多了吧!接下來我們分析一個不一樣的東西
看程式碼3:
#include <stdio.h>
struct stu{
char a;
double b;
char c;
};
int main(){
struct stu ss;
printf("%d\n", sizeof(ss));
printf("%p\n", &ss.a);
printf("%p\n", &ss.b);
printf("%p\n", &ss.c);
return 0;
}
我們可以很清楚的看明白上面這個結構體的成員變數是(char , double , char)
所以我們的執行結果是:
win7,32位,vc中:
win7,64位,vc中:
結果二:
win7,32位,dev-c++中
win7,64位,dev-c++中
接下來再看一個小的程式:
程式碼4:
#include <stdio.h>
struct stu{
char a;
int e;
double b;
char c;
};
int main(){
struct stu ss;
printf("%d\n", sizeof(ss));
printf("%p\n", &ss.a);
printf("%p\n", &ss.e);
printf("%p\n", &ss.b);
printf("%p\n", &ss.c);
return 0;
}
我們也可以直接看到:這個結構體成員是(char , int , double , char )
好,我們在看一下簡單地執行結果:
win7, 32, dev:
win7, 64, dev:
下面是win7,32,vc
下面是win7,64,vc
哈哈,不要害怕它們為什麼都是24,,接下來我們需要在看一下他們的記憶體分配情況,
下面這個圖是程式碼3的圖:
程式碼4的這個記憶體圖(將程式碼1和程式碼2的記憶體圖給結合起來了),
哈哈,太難畫了,我在這裡就不畫了啊,大家應該可以理解的
好,接著繼續分析記憶體情況
我們前面提到的在程式碼1,程式碼2中記憶體是簡單地4位元組對齊,所以我們一直在找4個位元組來存放他們(也就是簡單地說,char後面如果跟著int型別,那麼就會出現3個空閒),
而到了我們這個程式碼3, 4, 記憶體就是簡單地8位元組對齊,所以我們一直在找8個位元組來存放他們,(也就是簡單地說,char後面如果跟著double型別,那麼就會出現7個空閒)
這又是為了什麼?
這裡我們又要提一下記憶體的自然對齊了
自然對齊就是說:每一種資料型別都必須放在記憶體地址中的整數倍上,舉一個簡單地例子
(地址4)可以放char型別的,也可以放int型別,也可以放short,但是但是就是不可用存放double型別,僅僅只是因為它不是8的整數倍,OK, 就是這麼簡單。
(地址3)可以用來存放char型別,但是不可用用來存放int, short, double ,也只是因為它們不是整數倍關係
(宣告一點,地址0是任何型別整數倍的, 這一點,我們可以這樣記著,理解為,可以存放任何資料型別)
所以說:在windows下:
所以,此時也就很好解釋上面的幾種情況了,哪些空閒的記憶體塊,就是因為它們本身不是資料型別的整數倍,所以被留了下來,在加上為了訪問便利,所以才出現了記憶體對齊這麼一說的吧!(個人簡單理解),其實我們這樣想想,也就這麼回事
那麼在linux下又是什麼樣子的呢????
接下來我們繼續看:
程式碼5(在linux下):
#include <stdio.h>
#include <sys/cdefs.h>
struct str{
char a;
int b;
char c;
};
int main(){
struct str A;
printf("%u\n", sizeof(A));
printf("%p\n", &(A.a));
printf("%p\n", &(A.b));
printf("%p\n", &(A.c));
return 0;
}
這樣看,我們知道此時結構體裡面的成員型別是: char ,int , char
好,我們大家一起看執行結果:
linux(ubuntu),64位,:
linux(ubuntu),32位
這時,我們可以看到上面的記憶體分配是一樣的。就是說他們遵從的基本準則是一樣的(可以簡單的理解為:跟window下面是一樣的,(簡單的四位元組對齊))
好了,我們接下來繼續看另外一種情況:
程式碼6(在linux下)
#include <stdio.h>
#include <sys/cdefs.h>
struct str{
char a;
double b;
char c;
};
int main(){
struct str A;
printf("結構體總大小位:%lu\n", sizeof(A));
printf("成員a的初始地址:%p\n", &(A.a));
printf("成員b的初始地址:%p\n", &(A.b));
printf("成員c的初始地址:%p\n", &(A.c));
return 0;
}
這時,我們也可以很清楚的看到當前結構體的成員變數型別是: char , double , char
好了,我們接著往下看:執行結果:
liunx(ubuntu下),64位:
linux(ubuntu下), 32位:
哈哈,不一樣了吧,懵了嗎?不要緊,接著繼續看記憶體分配:
(下圖是64位的簡單記憶體分配圖)
下面是(32位的簡單記憶體分配圖)
好了,這回我們也看到了,在編譯器的分配下,
linux(64位) :它是跟window底下的說法是一樣的,對於double這個型別,它的初始地址就是僅僅只能在8的倍數的地方,也就是上面我們所說的遵從的是記憶體的自然對齊,所以(char後面是double的話,它會出現7個空閒區)
linux(32位):它是跟window是不一樣的,但是也很好理解,就是說,它的所有東西都是遵從(基本的4位元組對齊),而32位的編譯器,將其(double)處理成了兩個4位元組,所以才會出現上面的情況,或者說對於double而言編譯器不考慮它的自然對齊,(所以說char後面出現double的話,它會出現3個空閒區)
好了,大傢伙:
現在我們來總結一下:
對於windows,關於結構體也就是說,我們不用考慮什麼偏移量,什麼加什麼啊,那些都太麻煩了,只需要明白記憶體的自然對齊就ok了,(不管什麼32,64位)
然而,對於linux下,對於64位:跟window一樣就ok了,
而linux下,32位:就用簡單的4位元組對齊就0k了,遇到double的話,簡單的將其當作兩個4位元組就ok了。
(ps,哎呀嗎,太累了,畫圖太多了,真心不容易啊,但是麼事,心不累,哈哈!!!)
轉載自這位大俠:http://blog.csdn.net/msdnwolaile/article/details/50158463
相關文章
- 從 CPU 角度理解 Go 中的結構體記憶體對齊Go結構體記憶體
- 從 MMU 看記憶體管理記憶體
- 記憶體結構記憶體
- 深入理解 JVM 之 JVM 記憶體結構JVM記憶體
- 從萌新的角度理解JVM記憶體管理JVM記憶體
- Oracle體系結構之-記憶體結構Oracle記憶體
- struct結構體大小的計算(記憶體對齊)Struct結構體記憶體
- 結構體記憶體對齊結構體記憶體
- Oracle記憶體體系結構Oracle記憶體
- PostgreSQL:記憶體結構SQL記憶體
- oracle 記憶體結構Oracle記憶體
- JVM記憶體結構JVM記憶體
- 【基礎篇記憶體結構】oracle10g記憶體結構(一)記憶體Oracle
- Oracle體系結構:記憶體結構和程式結構(轉)Oracle記憶體
- 深入探究JVM之記憶體結構及字串常量池JVM記憶體字串
- MySQL整體架構與記憶體結構MySql架構記憶體
- Oracle體系結構之記憶體結構(SGA、PGA)Oracle記憶體
- 從JVM設計角度解讀Java記憶體模型JVMJava記憶體模型
- 從Java記憶體模型角度理解安全初始化Java記憶體模型
- oracle 記憶體結構(二)Oracle記憶體
- 調節記憶體結構記憶體
- Oracle 記憶體結構(一)Oracle記憶體
- ORACLE 記憶體結構理解.Oracle記憶體
- oracle 記憶體結構具體解釋Oracle記憶體
- 結構體的大小結構體
- 從C++看C#託管記憶體與非託管記憶體C++C#記憶體
- [Virtualization]ESXi體系結構與記憶體管理(一)體系結構記憶體
- Linux:深入淺出 Linux 共享記憶體Linux記憶體
- [Virtualization]ESXi體系結構與記憶體管理(二)控制記憶體分配記憶體
- [Virtualization]ESXi體系結構與記憶體管理(三)控制記憶體分配記憶體
- 深入理解JVM的記憶體結構及GC機制JVM記憶體GC
- 獲取Linux 記憶體頁大小的命令Linux記憶體
- 在linux檢視記憶體的大小(轉)Linux記憶體
- JVM記憶體結構、Java記憶體模型和Java物件模型JVM記憶體Java模型物件
- 《C++反彙編與逆向分析技術揭祕》讀書總結——從記憶體角度看繼承C++記憶體繼承
- 從實踐者的角度看軟體架構的歷史架構
- Linux堆記憶體管理深入分析Linux記憶體
- 理解JVM(一):記憶體結構JVM記憶體