C語言-結構體內存對齊

C語言結構體對齊也是老生常談的話題了。基本上是面試題的必考題。內容雖然很基礎,但一不小心就會弄錯。寫出一個struct,然后sizeof,你會不會經常對結果感到奇怪?sizeof的結果往往都比你聲明的變量總長度要大,這是怎么回事呢?

  開始學的時候,也被此類問題困擾很久。其實相關的文章很多,感覺說清楚的不多。結構體到底怎樣對齊?

  有人給對齊原則做過總結,具體在哪里看到現在已記不起來,這里引用一下前人的經驗(在沒有#pragma pack宏的情況下):

  原則1、數據成員對齊規則:結構(struct或聯合union)的數據成員,第一個數據成員放在offset為0的地方,以后每個數據成員存儲的起始位置要從該成員大小的整數倍開始(比如int在32位機為4字節,則要從4的整數倍地址開始存儲)。

  原則2、結構體作為成員:如果一個結構里有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲。(struct a里存有struct b,b里有char,int,double等元素,那b應該從8的整數倍開始存儲。)

  原則3、收尾工作:結構體的總大小,也就是sizeof的結果,必須是其內部最大成員的整數倍,不足的要補齊。

  這三個原則具體怎樣理解呢?我們看下面幾個例子,通過實例來加深理解。

????例1:

struct {short a1;short a2;short a3;
} A;struct{long a1;short a2;
} B;

????????? sizeof(A) = 6; 這個很好理解,三個short都為2。

????????? sizeof(B) = 8; 這個比是不是比預想的大2個字節?long為4,short為2,整個為8,因為原則3。

????例2:

struct A{int a;char b;short c;
};struct B{char b;int a;short c;
};

???????sizeof(A) = 8; int為4,char為1,short為2,這里用到了原則1和原則3。

?????? sizeof(B) = 12; 是否超出預想范圍?char為1,int為4,short為2,怎么會是12?還是原則1和原則3。

???深究一下,為什么是這樣,我們可以看看內存里的布局情況。

?????????????????a???????? b???????? c
???A的內存布局:1111,???? 1*,?????? 11

?????????????????b????????? a??????? c
???B的內存布局:1***,???? 1111,?? 11**

???其中星號*表示填充的字節。A中,b后面為何要補充一個字節?因為c為short,其起始位置要為2的倍數,就是原則1。c的后面沒有補充,因為b和c正好占用4個字節,整個A占用空間為4的倍數,也就是最大成員int類型的倍數,所以不用補充。

???B中,b是char為1,b后面補充了3個字節,因為a是int為4,根據原則1,起始位置要為4的倍數,所以b后面要補充3個字節。c后面補充兩個字節,根據原則3,整個B占用空間要為4的倍數,c后面不補充,整個B的空間為10,不符,所以要補充2個字節。

???再看一個結構中含有結構成員的例子:

?????? 例3:

struct A{int a;double b;float c;
};struct B{char e[2];int f;double g;short h;struct A i;
};

?????? sizeof(A) = 24; 這個比較好理解,int為4,double為8,float為4,總長為8的倍數,補齊,所以整個A為24。

?????? sizeof(B) = 48; 看看B的內存布局。

???????????????????????????????? e???????? f???????????? g??????????????? h??????????????????????????????????? i?
????B的內存布局:11* *,?? 1111,?? 11111111, 11 * * * * * *,??????? 1111* * * *, 11111111, 1111 * * * *

????i其實就是A的內存布局。i的起始位置要為24的倍數,所以h后面要補齊。把B的內存布局弄清楚,有關結構體的對齊方式基本就算掌握了。

????以上講的都是沒有#pragma pack宏的情況,如果有#pragma pack宏,對齊方式按照宏的定義來。比如上面的結構體前加#pragma pack(1),內存的布局就會完全改變。sizeof(A) = 16; sizeof(B) = 32;

????有了#pragma pack(1),內存不會再遵循原則1和原則3了,按1字節對齊。沒錯,這不是理想中的沒有內存對齊的世界嗎。

????????????????????????????????? a??????????????? b???????????? c
?????? A的內存布局:1111,???? 11111111,?? 1111

???????????????????????????????? e??????? f???????????? g????????? h???????????????????? i
?????? B的內存布局:11,?? 1111,?? 11111111, 11 ,??????????? 1111, 11111111, 1111

?????? 那#pragma pack(2)的結果又是多少呢?#pragma pack(4)呢?留給大家自己思考吧,相信沒有問題。

?????? 還有一種常見的情況,結構體中含位域字段。位域成員不能單獨被取sizeof值。C99規定int、unsigned int和bool可以作為位域類型,但編譯器幾乎都對此作了擴展,允許其它類型類型的存在。

?????? 使用位域的主要目的是壓縮存儲,其大致規則為:
?????? 1) 如果相鄰位域字段的類型相同,且其位寬之和小于類型的sizeof大小,則后面的字段將緊鄰前一個字段存儲,直到不能容納為止;
?????? 2) 如果相鄰位域字段的類型相同,但其位寬之和大于類型的sizeof大小,則后面的字段將從新的存儲單元開始,其偏移量為其類型大小的整數倍;
?????? 3) 如果相鄰的位域字段的類型不同,則各編譯器的具體實現有差異,VC6采取不壓縮方式,Dev-C++采取壓縮方式;
?????? 4) 如果位域字段之間穿插著非位域字段,則不進行壓縮;
?????? 5) 整個結構體的總大小為最寬基本類型成員大小的整數倍。

??????? 還是讓我們來看看例子。

?????? 例4:

struct A{char f1 : 3;char f2 : 4;char f3 : 5;
};

???????????????????? a????????b???????????? c
?????? A的內存布局:111,??? 1111 *,?? 11111 * * *

?????? 位域類型為char,第1個字節僅能容納下f1和f2,所以f2被壓縮到第1個字節中,而f3只能從下一個字節開始。因此sizeof(A)的結果為2。

?????? 例5:

struct B{char f1 : 3;short f2 : 4;char f3 : 5;
};

?????? 由于相鄰位域類型不同,在VC6中其sizeof為6,在Dev-C++中為2。

?????? 例6:

struct C{char f1 : 3;char f2;char f3 : 5;
};

?????? 非位域字段穿插在其中,不會產生壓縮,在VC6和Dev-C++中得到的大小均為3。

?????? 考慮一個問題,為什么要設計內存對齊的處理方式呢?如果體系結構是不對齊的,成員將會一個挨一個存儲,顯然對齊更浪費了空間。那么為什么要使用對齊呢?體 系結構的對齊和不對齊,是在時間和空間上的一個權衡。對齊節省了時間。假設一個體系結構的字長為w,那么它同時就假設了在這種體系結構上對寬度為w的數據 的處理最頻繁也是最重要的。它的設計也是從優先提高對w位數據操作的效率來考慮的。有興趣的可以google一下,人家就可以跟你解釋的,一大堆的道理。

?????? 最后順便提一點,在設計結構體的時候,一般會尊照一個習慣,就是把占用空間小的類型排在前面,占用空間大的類型排在后面,這樣可以相對節約一些對齊空間。

轉載于:https://www.cnblogs.com/JohnABC/p/4704789.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/397716.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/397716.shtml
英文地址,請注明出處:http://en.pswp.cn/news/397716.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

nginx 二進制包安裝mysql_二進制安裝mysql5.7

下載地址:https://downloads.mysql.com/archives/community/[rootlocalhost soft]# lsmysql-5.7.17-linux-glibc2.5-x86_64.tar.gz nginx-1.12.2 nginx-1.12.2.tar.gz[rootlocalhost soft]#1.詳細描安裝的過程1.1關閉防火墻systemctl stop firewalld.service #停止f…

.NET 類型(Types)的那些事

引言 您是.Net工程師?那 .NetFramework中的類型您知道有三大類嗎?(除了引用類型和值類型,還有?) 引用類型一定在“堆”上,值類型一定在“棧”上? 那引用類型在內存中的布局細節您又知…

幾種去除數組中重復元素的方法、數組去重

工作中遇到的一個問題&#xff0c;就是去除數組中重復的元素&#xff0c;記錄一下幾種有效的方法&#xff1a; 第一種思路&#xff1a;遍歷要刪除的數組arr, 把元素分別放入另一個數組tmp中&#xff0c;在判斷該元素在arr中不存在才允許放入tmp中。 <!DOCTYPE html> <…

MongoDB學習使用

一、什么是MongoDB&#xff1f; MongoDB是一個高性能&#xff0c;開源&#xff0c;無模式的文檔型數據庫&#xff0c;是當前NoSql數據庫中比較熱門的一種。它在許多場景下可用于替代傳統的關系型數據庫或鍵/值存儲方式&#xff0c; NoSql&#xff0c;全稱是 Not Only Sql,指的是…

域賬號更改密碼之后代理需要重新配置

在使用域賬號的時候&#xff0c;如果需要配置賬戶和密碼&#xff0c;那么最好記錄下來&#xff0c;否則將來找不到就很尷尬了。 我遇到的問題是&#xff0c;因為在另外一臺電腦配置了域賬號&#xff0c;用來聯網&#xff0c;提供網絡給visual studio 1.Firefox 這個代理的賬號…

wcf精通1-15

隨筆- 197 文章- 0 評論- 3407 十五天精通WCF——第一天 三種Binding讓你KO80%的業務 轉眼wcf技術已經出現很多年了&#xff0c;也在.net界混的風生水起&#xff0c;同時.net也是一個高度封裝的框架&#xff0c;作為在wcf食物鏈最頂端的我們所能做的任務已經簡單的不能再簡單…

python如何實現共享報表系統_使用python來實現報表自動化-阿里云開發者社區

xlwt 常用功能xlrd 常用功能xlutils 常用功能xlwt寫Excel時公式的應用xlwt寫入特定目錄(路徑設置)xlwt Python語言中&#xff0c;寫入Excel文件的擴展工具。可以實現指定表單、指定單元格的寫入。支持excel03版到excel2013版。使用時請確保已經安裝python環境。百度百科xlrd Py…

去除inline-block元素間間距的N種方法

這篇文章發布于 2012年04月24日&#xff0c;星期二&#xff0c;22:38&#xff0c;歸類于 css相關。 閱讀 147771 次, 今日 52 次 by zhangxinxu from http://www.zhangxinxu.com 本文地址&#xff1a;http://www.zhangxinxu.com/wordpress/?p2357 一、現象描述 真正意義上的in…

Docker深入淺出2

Docker系統架構 Docker使用客戶端-服務端&#xff08;c/s&#xff09;架構模式&#xff0c;使用遠程api來管理和創建Docker容器。 docker容器通過Docker鏡像來創建。 容器與鏡像的關系類似于面向對象編程中的對象與類的關系 Docker面向對象容器對象鏡像類加速器配置&#xff1a…

mysql安裝包下載密碼_MySQL解壓包的安裝與下載的圖文教程

這篇文章主要為大家詳細介紹了mysql解壓包的安裝基礎教程&#xff0c;具有一定的參考價值&#xff0c;感興趣的小伙伴們可以參考一下由于換了新電腦&#xff0c;所以的環境都要到新電腦去配置。突然發現mysql的配置忘了&#xff0c;然后百度又重新來一遍。特地寫一篇文章記錄一…

php 扒取網頁數據

扒取方法 public function index(){$url http://www.dytt8.net/;// $url Public/txt/movies.txt;$content file_get_contents($url);$content iconv("gb2312", "utf-8//IGNORE",$content);$reg "|<div class\"co_content2\">(.*…

多維DP UVA 11552 Fewest Flop

題目傳送門 1 /*2 題意&#xff1a;將子符串分成k組&#xff0c;每組的字符順序任意&#xff0c;問改變后的字符串最少有多少塊3 三維DP&#xff1a;可以知道&#xff0c;每一組的最少塊是確定的&#xff0c;問題就在于組與組之間可能會合并塊&#xff0c;總塊數會-1。…

多表聯合查詢

關聯數據庫字典表的多表聯合查詢 inner join…on 自動連接 需要用到表的所有信息時&#xff0c;可以用以下兩種方法 1) left join…on… 左連接 &#xff08;以左為準&#xff0c;右邊沒有NULL代替&#xff09; 2) right join…on… 右連接&#xff08;以右為準&#xff…

python elasticsearch update_使用python的elasticsearch部分更新

我有以下格式的elasticsearch文檔。我需要部分更新“x”字段并在其中添加python dict。{"_index": "gdata34","_type": "gdat","_id": "328091-72341-118","_version": 1,"_score": 1,"…

32位與64位注冊表

如果32位系統OFP的注冊表路徑是 HKEY_LOCAL_MACHINE\SOFTWARE\Bohemia Interactive\ 那么在64系統里就應該是 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Bohemia Interactive\ 多了一級Wow6432Node轉載于:https://www.cnblogs.com/zhang-pengcheng/p/4712135.html

http 請求頭和響應頭

客戶端發送請求過程帶著的數據&#xff1a; 1.請求地址 2.請求方式 3.請求頭 request headers 4.請求參數 https://www.juhe.cn/ 130.... 1a2b....pei 服務端響應給客戶端的信息&#xff1a; 1.響應內容 2.響應報文/響應頭部 response headers a 響應頭 b 響應體 3.http狀…

[算法]-排序算法之希爾排序

希爾排序算法思想 希爾排序的實質就是分組插入排序&#xff0c;該方法又稱縮小增量排序.基本思想是&#xff1a;先將整個待排元素序列分割成若干個子序列&#xff08;由相隔某個“增量”的元素組成的&#xff09;分別進行直接插入排序&#xff0c;然后依次縮減增量再進行排序&a…

python tkinter button顏色變不了_tkinter多按鈕顏色變化

我使用tkinter創建一個8x8按鈕矩陣&#xff0c;當按下單個按鈕時&#xff0c;它會添加到最終列表中(例如finalList((0,0)&#xff0c;(5,7)&#xff0c;(6,6)&#xff0c;…)&#xff0c;允許我快速創建8x8(x&#xff0c;y)坐標圖像。我已經創建了一個帶有按鈕的窗口&#xff0…

應用spss可靠性分析軟件

問卷調查的可靠性分析 一、概念&#xff1a; 信度是指依據測驗工具所得到的結果的一致性或穩定性&#xff0c;反映被測特征真實程度的指標。一般而言&#xff0c;兩次或兩個測驗的結果愈是一致。則誤差愈小&#xff0c;所得的信度愈高&#xff0c;它具有下面特性&#xff1a;1、…

springmvc中的單例問題

1&#xff0c;springmvc實際上是基于一個叫做DispatcherServlet的servlet的。servlet按照以往的學習經驗&#xff0c;他是單事例多線程的。 Servlet生命周期 1.裝載Servlet。這項操作一般是動態執行的。然而&#xff0c;Server通常會提供一個管理的選項&#xff0c;用于在Serve…