欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語(yǔ)言結(jié)構(gòu)體內(nèi)存對(duì)齊問(wèn)題小結(jié)

 更新時(shí)間:2025年03月24日 11:38:30   作者:李白同學(xué)  
本文主要講解了C語(yǔ)言中結(jié)構(gòu)體的內(nèi)存對(duì)齊規(guī)則、計(jì)算方法以及影響因素,包括對(duì)齊規(guī)則的四個(gè)要點(diǎn)、內(nèi)存對(duì)齊的原因、如何修改默認(rèn)對(duì)齊數(shù)以及結(jié)構(gòu)體傳參時(shí)的注意事項(xiàng),此外,還介紹了結(jié)構(gòu)體位段的概念、內(nèi)存分配和使用注意事項(xiàng),感興趣的朋友一起看看吧

1.結(jié)構(gòu)體內(nèi)存對(duì)齊

我們已經(jīng)基本掌握了結(jié)構(gòu)體的使用了。那我們現(xiàn)在必須得知道結(jié)構(gòu)體在內(nèi)存中是如何存儲(chǔ)的??jī)?nèi)存是如何分配的?所以我們得知道如何計(jì)算結(jié)構(gòu)體的大???這就引出了我們今天所要探討的內(nèi)容:結(jié)構(gòu)體內(nèi)存對(duì)齊。

1.1 對(duì)齊規(guī)則

首先得掌握結(jié)構(gòu)體的對(duì)齊規(guī)則:
1. 結(jié)構(gòu)體的第?個(gè)成員對(duì)?到和結(jié)構(gòu)體變量起始位置偏移量為0的地址處。
2. 其他成員變量要對(duì)?到某個(gè)數(shù)字(對(duì)?數(shù))的整數(shù)倍的地址處。
對(duì)齊數(shù) = 編譯器默認(rèn)的?個(gè)對(duì)?數(shù) 與 該成員變量大小的 較?值。
- VS 中默認(rèn)對(duì)齊數(shù)的值為 8
- Linux中 gcc 沒(méi)有默認(rèn)對(duì)?數(shù),對(duì)?數(shù)就是成員??的大小
3. 結(jié)構(gòu)體總大小為最?對(duì)?數(shù)(結(jié)構(gòu)體中每個(gè)成員變量都有?個(gè)對(duì)?數(shù),所有對(duì)?數(shù)中最?的)的
整數(shù)倍。
4. 如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體成員對(duì)?到??的成員中最?對(duì)?數(shù)的整數(shù)倍處,結(jié)構(gòu)
體的整體??就是所有最?對(duì)?數(shù)(含嵌套結(jié)構(gòu)體中成員的對(duì)?數(shù))的整數(shù)倍。

范例1:

//范例1
struct S1
{
	char c1;//1 8 1
	int i;  //4 8 4
	char c2;//1 8 1
};
int main()
{
	struct S1 s1 = { 0 };
	printf("%zd\n", sizeof(s1));
	return 0;
}

我們畫圖分析一下:

15aa98d199d8432ab9f0d6c61cdd4f6f.png

我們運(yùn)行一下結(jié)果看看,是不是12個(gè)字節(jié):

ad308321e09142eebe4307b078b41f7c.png

確實(shí)是12個(gè)字節(jié),這就說(shuō)明,結(jié)構(gòu)體在內(nèi)存存儲(chǔ)中,存在內(nèi)存對(duì)齊的原則。

范例2:

//范例2
struct S2
{
	char c1;
	char c2;
	int i;
};
int main()
{
	struct S2 s2 = { 0 };
	printf("%zd\n", sizeof(s2));
	return 0;
}

同樣的道理:

f4f48a75f2c14e3ba8d8a82a023c9309.png

運(yùn)行結(jié)果:

3d07f9a5b61d4d03a514ca6385a888f1.png

范例3:

//范例3
struct S3
{
	double d;//8 8 8
	char c;  //1 8 1
	int i;   //4 8 4
};
int main()
{
	struct S3 s3 = { 0 };
	printf("%zd\n", sizeof(s3));
	return 0;
}

08e42c74535f427aa4faf99a13703b04.png

運(yùn)行結(jié)果:

7d29cbd2cc934f93bfdefabb731ff858.png

范例4:

//范例4
struct S3
{
	double d;//8 8 8
	char c;  //1 8 1
	int i;   //4 8 4
};
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};
int main()
{
	struct S4 s4 = { 0 };
	printf("%zd\n", sizeof(s4));
	return 0;
}

65519e877cb049b681f6aee2312cfd28.png

運(yùn)行結(jié)果:

feceb090dd354b35bc2b3479e5262748.png

1.2 為什么存在內(nèi)存對(duì)齊?

?部分的參考資料都是這樣說(shuō)的:
1. 平臺(tái)原因 (移植原因):
不是所有的硬件平臺(tái)都能訪問(wèn)任意地址上的任意數(shù)據(jù)的;某些硬件平臺(tái)只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。
2.性能原因:
數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在?然邊界上對(duì)?。原因在于,為了訪問(wèn)未對(duì)?的內(nèi)存,處理器需要作兩次內(nèi)存訪問(wèn);?對(duì)?的內(nèi)存訪問(wèn)僅需要?次訪問(wèn)。假設(shè)?個(gè)處理器總是從內(nèi)存中取8個(gè)字節(jié),則地 址必須是8的倍數(shù)。如果我們能保證將所有的double類型的數(shù)據(jù)的地址都對(duì)?成8的倍數(shù),那么就可以??個(gè)內(nèi)存操作來(lái)讀或者寫值了。否則,我們可能需要執(zhí)?兩次內(nèi)存訪問(wèn),因?yàn)閷?duì)象可能被分放在兩個(gè)8字節(jié)內(nèi)存塊中。
總體來(lái)說(shuō):結(jié)構(gòu)體的內(nèi)存對(duì)?是拿空間來(lái)?yè)Q取時(shí)間的做法。
那在設(shè)計(jì)結(jié)構(gòu)體的時(shí)候,我們既要滿?對(duì)?,?要節(jié)省空間,如何做到:
讓占?空間?的成員盡量集中在?起

 //例如:
 struct S1
 {
     char c1;//1 8 1
     int i;  //4 8 4
     char c2;//1 8 1
 };
//sizeof(struct S1) -> 12個(gè)字節(jié)
 struct S2
 {
     char c1;//1 8 1
     char c2;//1 8 1
     int i;  //4 8 4
 };
//sizeof(struct S2) -> 8個(gè)字節(jié)

1.3 修改默認(rèn)對(duì)齊數(shù) #pragma 這個(gè)預(yù)處理指令,可以改變編譯器的默認(rèn)對(duì)齊數(shù)。

#include <stdio.h>
#pragma pack(1)//設(shè)置默認(rèn)對(duì)?數(shù)為1
struct S
{
 char c1;
 int i;
 char c2;
};
#pragma pack()//取消設(shè)置的對(duì)?數(shù),還原為默認(rèn)
int main()
{
 //輸出的結(jié)果是什么?
 printf("%d\n", sizeof(struct S));
 return 0;
}

結(jié)構(gòu)體在對(duì)齊方式不合適的時(shí)候,我們可以自己更改默認(rèn)對(duì)齊數(shù)。

運(yùn)行結(jié)果:

ab6eb5410408467199d9cc00b576a0dc.png

2.結(jié)構(gòu)體傳參

struct S
{
    int data[1000];
    int num;
};
struct S s = {{1,2,3,4}, 1000};
//結(jié)構(gòu)體傳參
void print1(struct S s)
{
     printf("%d\n", s.num);
}
//結(jié)構(gòu)體地址傳參
void print2(struct S* ps)
{
     printf("%d\n", ps->num);
}
int main()
{
     print1(s); //傳結(jié)構(gòu)體
     print2(&s); //傳地址
     return 0;
}

上?的 print1 和 print2 函數(shù)哪個(gè)好些?
答案是:首選print2函數(shù)。
原因:
函數(shù)傳參的時(shí)候,參數(shù)是需要壓棧,會(huì)有時(shí)間和空間上的系統(tǒng)開(kāi)銷。
如果傳遞?個(gè)結(jié)構(gòu)體對(duì)象的時(shí)候,結(jié)構(gòu)體過(guò)?,參數(shù)壓棧的的系統(tǒng)開(kāi)銷?較?,所以會(huì)導(dǎo)致性能的下降。
結(jié)論:
結(jié)構(gòu)體傳參的時(shí)候,要傳結(jié)構(gòu)體的地址。

3.結(jié)構(gòu)體實(shí)現(xiàn)位段

結(jié)構(gòu)體講完就得講講結(jié)構(gòu)體實(shí)現(xiàn)位段的能力。

3.1 什么是位段

位段的聲明和結(jié)構(gòu)是類似的,有兩個(gè)不同:
1. 位段的成員必須是 int、unsigned int 或signed int ,在C99中位段成員的類型也可以
    選擇其他類型。
2. 位段的成員名后邊有?個(gè)冒號(hào)和?個(gè)數(shù)字。
比如:

struct A
{
 int _a:2;
 int _b:5;
 int _c:10;
 int _d:30;
};

A就是?個(gè)位段類型。 那位段A所占內(nèi)存的大小是多少?

printf("%d\n", sizeof(struct A));

3.2 位段的內(nèi)存分配

1. 位段的成員可以是 int 、 unsigned int 、 signed int 或者是 char 等類型
2. 位段的空間上是按照需要以4個(gè)字節(jié)( int )或者1個(gè)字節(jié)( char )的?式來(lái)開(kāi)辟的。
3. 位段涉及很多不確定因素,位段是不跨平臺(tái)的,注重可移植的程序應(yīng)該避免使?位段。

//?個(gè)例?
#include <stdio.h>
struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	//空間是如何開(kāi)辟的?
	return 0;
}

3.3 位段的跨平臺(tái)問(wèn)題

1. int 位段被當(dāng)成有符號(hào)數(shù)還是?符號(hào)數(shù)是不確定的。
2. 位段中最?位的數(shù)目不能確定。(16位機(jī)器最大16,32位機(jī)器最大32,寫成27,在16位機(jī)器會(huì)
    出問(wèn)題。
3. 位段中的成員在內(nèi)存中從左向右分配,還是從右向左分配,標(biāo)準(zhǔn)尚未定義。
4. 當(dāng)?個(gè)結(jié)構(gòu)包含兩個(gè)位段,第?個(gè)位段成員?較大,?法容納于第?個(gè)位段剩余的位時(shí),是舍棄
    剩余的位還是利?,這是不確定的。
總結(jié):
跟結(jié)構(gòu)相?,位段可以達(dá)到同樣的效果,并且可以很好的節(jié)省空間,但是有跨平臺(tái)的問(wèn)題存在。

3.4 位段使用的注意事項(xiàng)

位段的?個(gè)成員共有同?個(gè)字節(jié),這樣有些成員的起始位置并不是某個(gè)字節(jié)的起始位置,那么這些位置處是沒(méi)有地址的。內(nèi)存中每個(gè)字節(jié)分配?個(gè)地址,?個(gè)字節(jié)內(nèi)部的bit位是沒(méi)有地址的。 所以不能對(duì)位段的成員使?&操作符,這樣就不能使?scanf直接給位段的成員輸?值,只能是先輸?放在?個(gè)變量中,然后賦值給位段的成員。

struct A
{
    int _a : 2;
    int _b : 5;
    int _c : 10;
    int _d : 30;
};
int main()
{
    struct A sa = {0};
    scanf("%d", &sa._b);//這是錯(cuò)誤的
    //正確的?范
    int b = 0;
    scanf("%d", &b);
    sa._b = b;
    return 0;
}

到此這篇關(guān)于C語(yǔ)言結(jié)構(gòu)體內(nèi)存對(duì)齊問(wèn)題的文章就介紹到這了,更多相關(guān)C語(yǔ)言結(jié)構(gòu)體內(nèi)存對(duì)齊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Visual Studio Code (VSCode) 配置搭建 C/C++ 開(kāi)發(fā)編譯環(huán)境的流程

    Visual Studio Code (VSCode) 配置搭建 C/C++ 開(kāi)發(fā)編譯環(huán)境的流程

    記得N年前剛開(kāi)始接觸編程時(shí),使用的是Visual C++6.0,下面這個(gè)可愛(ài)的圖標(biāo)很多人一定很熟悉。不過(guò)今天想嘗鮮新的工具 Visual Studio Code 來(lái)搭建C/C++開(kāi)發(fā)環(huán)境,感興趣的朋友一起看看吧
    2021-09-09
  • C語(yǔ)言實(shí)現(xiàn)歌曲信息管理系統(tǒng)

    C語(yǔ)言實(shí)現(xiàn)歌曲信息管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)歌曲信息管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • C語(yǔ)言 Freertos的遞歸鎖詳解

    C語(yǔ)言 Freertos的遞歸鎖詳解

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言的遞歸鎖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • C語(yǔ)言實(shí)現(xiàn)紙牌24點(diǎn)小游戲

    C語(yǔ)言實(shí)現(xiàn)紙牌24點(diǎn)小游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)紙牌24點(diǎn)小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • c/c++基礎(chǔ)簡(jiǎn)單易懂的快速排序算法

    c/c++基礎(chǔ)簡(jiǎn)單易懂的快速排序算法

    這篇文章主要為大家介紹了c/c++基礎(chǔ)非常簡(jiǎn)單易懂的快速排序算法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2021-11-11
  • C++實(shí)現(xiàn)重載矩陣的部分運(yùn)算符

    C++實(shí)現(xiàn)重載矩陣的部分運(yùn)算符

    這篇文章主要為大家詳細(xì)介紹了如何利用C++實(shí)現(xiàn)重載矩陣的部分運(yùn)算符,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C++有一定幫助,需要的可以參考一下
    2022-10-10
  • opencv實(shí)現(xiàn)讀取視頻保存視頻

    opencv實(shí)現(xiàn)讀取視頻保存視頻

    這篇文章主要為大家詳細(xì)介紹了opencv實(shí)現(xiàn)讀取視頻保存視頻,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • c語(yǔ)言全盤搜索指定文件的實(shí)例代碼

    c語(yǔ)言全盤搜索指定文件的實(shí)例代碼

    c語(yǔ)言全盤搜索指定文件的實(shí)例代碼,需要的朋友可以參考一下
    2013-03-03
  • C語(yǔ)言報(bào)錯(cuò):Undefined Reference的產(chǎn)生原因和解決方案

    C語(yǔ)言報(bào)錯(cuò):Undefined Reference的產(chǎn)生原因和解決方案

    Undefined Reference(未定義引用)是C語(yǔ)言編譯過(guò)程中常見(jiàn)的錯(cuò)誤之一,通常在鏈接階段出現(xiàn),本文將詳細(xì)介紹Undefined Reference的產(chǎn)生原因,提供多種解決方案,并通過(guò)實(shí)例代碼演示如何有效避免和解決此類錯(cuò)誤,需要的朋友可以參考下
    2024-06-06
  • c++中l(wèi)og4cplus日志庫(kù)使用的基本步驟和示例代碼

    c++中l(wèi)og4cplus日志庫(kù)使用的基本步驟和示例代碼

    這篇文章主要給大家介紹了關(guān)于c++中l(wèi)og4cplus日志庫(kù)使用的相關(guān)資料,log4cplus是一款開(kāi)源的c++日志庫(kù),具有線程安全,靈活,以及多粒度控制的特點(diǎn),log4cplus可以將日志按照優(yōu)先級(jí)進(jìn)行劃分,使其可以面向程序的調(diào)試,運(yùn)行,測(cè)試,后期維護(hù)等軟件全生命周期,需要的朋友可以參考下
    2024-06-06

最新評(píng)論