1、內存對齊linux
(一)、爲何會有內存對齊?c++
一、爲了提升程序的性能,數據結構(尤爲是棧)應該儘量的在天然邊界上對齊。緣由是爲了訪問未對齊的內存,處理器須要進行兩次訪問,而訪問對齊的內存,只須要一次就夠了。這種方式稱做「以空間換時間」在不少對時間複雜度有要求問題中,會採用這種方法。數組
二、內存對齊可以增長程序的可移植性,由於不是全部的平臺都能隨意的訪問內存,有些平臺只能在特定的地址到處讀取內存。數據結構
通常狀況下內存對齊是編譯器的事情,咱們不須要考慮,但有些問題仍是須要考慮的,畢竟c/c++是直接操做內存的語言,須要瞭解程序在內存中的分佈和運行原理。ide
(二)、內存對齊:那麼如何對齊呢?性能
對齊原則:數據存放的起始位置是自身大小的整數倍處。指針
例:內存從0地址處開始。調試
char1個字節,因此對齊後它能夠存放到地址是1的倍數處。內存
short2個字節,因此對齊後它能夠存放到地址是2的倍數處。編譯器
int是4個字節,因此對齊後它能夠存放到地址是4的倍數處。
double是8個字節,因此對齊後它能夠存放到地址是8的倍數處。
如今相信你已經明白了內存對齊的原則,接下來咱們看看結構體內存對齊。
(三)、在瞭解結構體內存對齊以前先來了解幾個概念:
一、默認對齊數:在vs下內存對齊數默認是8,linux是4.能夠經過#program pack ()來修改默認對齊數。
二、偏移:相對於起始位置的的位置。例如起始位置是2,那麼2就是0偏移處,3就是1偏移處。
三、對齊數:變量自身的大小和默認對齊數之中的最小值。假設默認對齊數是8,int類型的對齊數就是4.由於int大小是4,小於8
2、結構體內存對齊原則:
一、結構或聯合的數據成員,第一個成員放到0偏移的地方,之後每一個數據成員都放到自身對齊數的整數倍偏移處。
二、結構體的大小必須是最大對齊數的整數倍。
例1:
struct stu
{
char c; //對齊數是1
short b; //對齊數是2
double d; //對齊數是8
int i; //對齊數是4
};
例2:
struct stu
{
char c; //對齊數是1
short b; //對齊數是2
struct A
{
double d; //對齊數是8
};
int i; //對齊數是4
}s;
嵌套結構體的大小,其分析方法仍是同樣,最大對齊數是8,sizeof(s)=24。
3、自定義類型
(一)、結構體聲明
一、沒有標籤,不完整的聲明。同時還定義一個變量。
struct
{
charc;
shortb;
inti;
}t1;
二、有標籤的聲明,但沒定義變量的聲明。
struct A
{
charc;
shortb;
inti;
};
//定義一個變量struct A *s1;
//注意,在同一個程序中,同時聲明一、2兩個結構體,則一、2兩個結構體會被認爲是不一樣類型的。因此 s1=&t1是錯誤的。
三、有標籤的聲明,同時還定義一個變量。
struct A
{
charc;
shortb;
inti;
}t3;
四、聲明的同時對結構體重命名爲A.
typedef struct A
{
charc;
shortb;
inti;
}A;
五、先有雞仍是先有蛋
struct B //不管哪一個放到前面都不對
{
structAa;
}s;
structA
{
structBb;
};
若是兩個結構體相互嵌套,則在聲明的時候須要對其中一個結構體進行不完整的聲明。
structA;
struct B
{
structAa;
}s;
structA
{
structBb;
};
(二)、結構體的初始化:
例如:
typedef struct A
{
charc;
shortb;
inti;
}A;
As1 = {'c', 2 , 4 };
(三)、結構體的自引用:(結構體的自引用一般會用在鏈表這種線性結構中用到)
一、錯誤的自引用方式,很容易理解的,結構體裏面又有結構體,這樣一直循環下去。(從前有座廟,廟裏有個老和尚,老和尚給小和尚講故事..........^v^)
typedef struct A
{
intdata;
structAn; //死循環
}A;
二、錯誤的只引用,由於結構體被從新命名爲A是在引用以後。
typedef struct //在結構體自引用的時候標籤不能省略。
{
intdata;
An; //必須使用完整的結構體名稱
}A;
三、正確的方式
typedef struct A
{
intdata;
structA *n; //用完整的結構體名稱,聲明一個結構體指針,
}A;
(四)、結構體作參數傳遞的效率:
當結構體很大時,結構體在做爲參數傳遞時,咱們傳遞它的地址,這樣可以提升效率,若是你不想改變結構體內容,則在形參處加上const就行。
(五)、柔性數組:
在結構體中最後一個成員容許是未知大小的數組,這個數組成爲柔性數組(柔性數組以前至少有一個成員變量)
typedef struct A
{
inti;
char a[];
}A;
含有柔性數組的結構體大:這樣的結構體,它的大小不包括柔性數組,因此sizeof(A)=4;空結構體的大小是1;
(六)、位段(位域):
一、概念:在一個結構體中以位爲單位來指定成員所佔內存的實際大小,這種以位爲單位的成員咱們稱爲位段,位段是一種特殊的結構體,位段的聲明和任何普通的結構體成員聲明相似,以下:
Struct 位段結構體名
{
Unsigned 位段名:位段長度;
Unsigned 位段名:位段長度;
………………..
Unsigned 位段名:位段長度;
}位段結構體變量名;
但有兩個例外,首先位段成員必須聲明成int ,unsigned int, signed int,。其次,在成員的後面是一個冒號和一個整數,這個整數指定該位段所佔用位的個數。(實際驗證後發現char類型也能夠,可是注意,位段中不能將int 和char 混合使用)。
二、 位段使用時須要注意是:
一、位段結構體中的成員不能使用數組和指針,但結構體變量可使數組或者指針。
二、由於數組和指針都是以字節爲單位的,同理也不能用&獲取位段的地址。
三、位段不支持移植。
例1:聲明一個位段,咱們先來分析一下他在計算機裏面是如何存儲的(一個無符號的int是4字節)。
struct tagAAA
{
unsigned int a : 1;
unsigned int b : 2;
unsigned int c : 6;
unsigned int d : 4;
unsigned int e;
}AAA_S;
由此咱們能夠明白位段的優勢,原本定義了5個成員,須要5個存儲單位,可是使用位段後只須要4個存儲空間就足夠了。
三、優勢:
但它的成員是一個或多個位的字段,這些不一樣長度的字段其實是存儲於一個或多個×××變量中,他的優勢是可以以較少的內存單元存儲數據。位段能夠用×××形式輸出。
例2:
struct tagAAA
{
unsigned int a : 1;
unsigned int : 2; //沒有聲明變量,可是卻指定位段大小,稱爲佔位。
unsigned int c : 6;
unsigned int d : 4;
unsigned int e; //沒有指定位段大小,默認爲自身類型的大小
}AAA_S;
(七)、聯合
一、聯合的聲明:
typedefunionA
{
inti;
charc;
}A;
二、聯合的特色:
聯合成員之間共用同一塊空間。聯合的大小等於成員中所佔內存最大變量大小。能夠用來測大小端。
(八)、枚舉:
一、聲明:
typedefenumA
{
zero,
one,
two
}A;
若是沒有對枚舉成員進行初始化時,則默認枚舉成員從0開始依次遞增
注意:
一、在同一個程序中,不能不能聲明同名的枚舉類型
二、在同一個程序中,不一樣的枚舉類型的枚舉成員不能同名。
三、任何枚舉的大小都是4
二、枚舉與#define 標識符之間區別:
一、#define 標識符在預編譯期間進行簡單替換。枚舉類型在編譯的時候肯定其值。
二、枚舉常量能夠調試,#define 標識符不能夠。
三、枚舉一次能夠定義大量的枚舉量。