您好,登錄后才能下訂單哦!
一、內(nèi)存對齊
(一)、為什么會有內(nèi)存對齊?
1、為了提高程序的性能,數(shù)據(jù)結構(尤其是棧)應該盡可能的在自然邊界上對齊。原因是為了訪問未對齊的內(nèi)存,處理器需要進行兩次訪問,而訪問對齊的內(nèi)存,只需要一次就夠了。這種方式稱作“以空間換時間”在很多對時間復雜度有要求問題中,會采用這種方法。
2、內(nèi)存對齊能夠增加程序的可移植性,因為不是所有的平臺都能隨意的訪問內(nèi)存,有些平臺只能在特定的地址處處讀取內(nèi)存。
一般情況下內(nèi)存對齊是編譯器的事情,我們不需要考慮,但有些問題還是需要考慮的,畢竟c/c++是直接操作內(nèi)存的語言,需要了解程序在內(nèi)存中的分布和運行原理。
(二)、內(nèi)存對齊:那么如何對齊呢?
對齊原則:數(shù)據(jù)存放的起始位置是自身大小的整數(shù)倍處。
例:內(nèi)存從0地址處開始。
char1個字節(jié),所以對齊后它可以存放到地址是1的倍數(shù)處。
short2個字節(jié),所以對齊后它可以存放到地址是2的倍數(shù)處。
int是4個字節(jié),所以對齊后它可以存放到地址是4的倍數(shù)處。
double是8個字節(jié),所以對齊后它可以存放到地址是8的倍數(shù)處。
現(xiàn)在相信你已經(jīng)明白了內(nèi)存對齊的原則,接下來我們看看結構體內(nèi)存對齊。
(三)、在了解結構體內(nèi)存對齊之前先來了解幾個概念:
1、默認對齊數(shù):在vs下內(nèi)存對齊數(shù)默認是8,linux是4.可以通過#program pack ()來修改默認對齊數(shù)。
2、偏移:相對于起始位置的的位置。例如起始位置是2,那么2就是0偏移處,3就是1偏移處。
3、對齊數(shù):變量自身的大小和默認對齊數(shù)之中的最小值。假設默認對齊數(shù)是8,int類型的對齊數(shù)就是4.因為int大小是4,小于8
二、結構體內(nèi)存對齊原則:
1、結構或聯(lián)合的數(shù)據(jù)成員,第一個成員放到0偏移的地方,以后每個數(shù)據(jù)成員都放到自身對齊數(shù)的整數(shù)倍偏移處。
2、結構體的大小必須是最大對齊數(shù)的整數(shù)倍。
例1:
struct stu
{
char c; //對齊數(shù)是1
short b; //對齊數(shù)是2
double d; //對齊數(shù)是8
int i; //對齊數(shù)是4
};
例2:
struct stu
{
char c; //對齊數(shù)是1
short b; //對齊數(shù)是2
struct A
{
double d; //對齊數(shù)是8
};
int i; //對齊數(shù)是4
}s;
嵌套結構體的大小,其分析方法還是一樣,最大對齊數(shù)是8,sizeof(s)=24。
三、自定義類型
(一)、結構體聲明
1、沒有標簽,不完整的聲明。同時還定義一個變量。
struct
{
charc;
shortb;
inti;
}t1;
2、有標簽的聲明,但沒定義變量的聲明。
struct A
{
charc;
shortb;
inti;
};
//定義一個變量struct A *s1;
//注意,在同一個程序中,同時聲明1、2兩個結構體,則1、2兩個結構體會被認為是不同類型的。所以 s1=&t1是錯誤的。
3、有標簽的聲明,同時還定義一個變量。
struct A
{
charc;
shortb;
inti;
}t3;
4、聲明的同時對結構體重命名為A.
typedef struct A
{
charc;
shortb;
inti;
}A;
5、先有雞還是先有蛋
struct B //無論哪個放到前面都不對
{
structAa;
}s;
structA
{
structBb;
};
如果兩個結構體相互嵌套,則在聲明的時候需要對其中一個結構體進行不完整的聲明。
structA;
struct B
{
structAa;
}s;
structA
{
structBb;
};
(二)、結構體的初始化:
例如:
typedef struct A
{
charc;
shortb;
inti;
}A;
As1 = {'c', 2 , 4 };
(三)、結構體的自引用:(結構體的自引用通常會用在鏈表這種線性結構中用到)
1、錯誤的自引用方式,很容易理解的,結構體里面又有結構體,這樣一直循環(huán)下去。(從前有座廟,廟里有個老和尚,老和尚給小和尚講故事..........^v^)
typedef struct A
{
intdata;
structAn; //死循環(huán)
}A;
2、錯誤的只引用,因為結構體被重新命名為A是在引用之后。
typedef struct //在結構體自引用的時候標簽不能省略。
{
intdata;
An; //必須使用完整的結構體名稱
}A;
3、正確的方式
typedef struct A
{
intdata;
structA *n; //用完整的結構體名稱,聲明一個結構體指針,
}A;
(四)、結構體做參數(shù)傳遞的效率:
當結構體很大時,結構體在作為參數(shù)傳遞時,我們傳遞它的地址,這樣能夠提高效率,如果你不想改變結構體內(nèi)容,則在形參處加上const就行。
(五)、柔性數(shù)組:
在結構體中最后一個成員允許是未知大小的數(shù)組,這個數(shù)組成為柔性數(shù)組(柔性數(shù)組之前至少有一個成員變量)
typedef struct A
{
inti;
char a[];
}A;
含有柔性數(shù)組的結構體大:這樣的結構體,它的大小不包括柔性數(shù)組,所以sizeof(A)=4;空結構體的大小是1;
(六)、位段(位域):
1、概念:在一個結構體中以位為單位來指定成員所占內(nèi)存的實際大小,這種以位為單位的成員我們稱為位段,位段是一種特殊的結構體,位段的聲明和任何普通的結構體成員聲明類似,如下:
Struct 位段結構體名
{
Unsigned 位段名:位段長度;
Unsigned 位段名:位段長度;
………………..
Unsigned 位段名:位段長度;
}位段結構體變量名;
但有兩個例外,首先位段成員必須聲明成int ,unsigned int, signed int,。其次,在成員的后面是一個冒號和一個整數(shù),這個整數(shù)指定該位段所占用位的個數(shù)。(實際驗證后發(fā)現(xiàn)char類型也可以,但是注意,位段中不能將int 和char 混合使用)。
2、 位段使用時需要注意是:
1、位段結構體中的成員不能使用數(shù)組和指針,但結構體變量可以使數(shù)組或者指針。
2、因為數(shù)組和指針都是以字節(jié)為單位的,同理也不能用&獲取位段的地址。
3、位段不支持移植。
例1:聲明一個位段,我們先來分析一下他在計算機里面是如何存儲的(一個無符號的int是4字節(jié))。
struct tagAAA
{
unsigned int a : 1;
unsigned int b : 2;
unsigned int c : 6;
unsigned int d : 4;
unsigned int e;
}AAA_S;
由此我們可以明白位段的優(yōu)點,本來定義了5個成員,需要5個存儲單位,但是使用位段后只需要4個存儲空間就足夠了。
3、優(yōu)點:
但它的成員是一個或多個位的字段,這些不同長度的字段實際上是存儲于一個或多個×××變量中,他的優(yōu)點是能夠以較少的內(nèi)存單元存儲數(shù)據(jù)。位段可以用×××形式輸出。
例2:
struct tagAAA
{
unsigned int a : 1;
unsigned int : 2; //沒有聲明變量,但是卻指定位段大小,稱為占位。
unsigned int c : 6;
unsigned int d : 4;
unsigned int e; //沒有指定位段大小,默認為自身類型的大小
}AAA_S;
(七)、聯(lián)合
1、聯(lián)合的聲明:
typedefunionA
{
inti;
charc;
}A;
2、聯(lián)合的特點:
聯(lián)合成員之間共用同一塊空間。聯(lián)合的大小等于成員中所占內(nèi)存最大變量大小。可以用來測大小端。
(八)、枚舉:
1、聲明:
typedefenumA
{
zero,
one,
two
}A;
如果沒有對枚舉成員進行初始化時,則默認枚舉成員從0開始依次遞增
注意:
1、在同一個程序中,不能不能聲明同名的枚舉類型
2、在同一個程序中,不同的枚舉類型的枚舉成員不能同名。
3、任何枚舉的大小都是4
2、枚舉與#define 標識符之間區(qū)別:
1、#define 標識符在預編譯期間進行簡單替換。枚舉類型在編譯的時候確定其值。
2、枚舉常量可以調(diào)試,#define 標識符不可以。
3、枚舉一次可以定義大量的枚舉量。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。