C++ 内存对齐 及 引用是否真的节省内存的一点思考
文章目錄
- 1. 內存對齊
- 2. 遞歸中的內存對齊
- 3. C++引用的本質
- 4. 致謝
1. 內存對齊
通過以下語句,獲取變量的占用內存打下:
cout << "size of int " << sizeof(int) << endl; cout << "size of int& " << sizeof(int&) << endl; cout << "size of char " << sizeof(char) << endl;int 和 int& 都占 4 字節, char 占 1 字節
編寫一個類:其包含1個 int,2個 char,排列順序不同。
class memory1 {int id;char a;char b; }; class memory2 {char a;int id;char b; }; class memory3 {char a;char b;int id; };可以看見,他們的占用空間大小是不一樣的。
size of memory1 8 size of memory2 12 size of memory3 8- 計算機從內存讀取數據是按塊讀取的,一般是4或者8的倍數一塊,一起讀取
- CPU和內存IO的硬件限制導致沒辦法將一個數據類型分在兩個塊中讀取
- 內存對齊,可以加快程序的運行速度,一般編譯器會在后臺進行內存對齊優化,但是也不能做到十分完美
- 內存對齊的參數可以更改,#pragma pack(n),n = 1,2,4,8,16
所以上面的memory類的內存對齊是按照4字節進行的,計算機按照順序分配內存,4字節剩余空間能放下某個類型的,就放進去,放不進去的,新往下找一塊4字節的空間放 int
加入#pragma pack(1),可見就是緊密排列了。
size of memory1 6 size of memory2 6 size of memory3 6 #pragma pack(2) size of memory1 6 size of memory2 8 size of memory3 62. 遞歸中的內存對齊
我在做LeetCode題的時候遇到一個遞歸爆棧問題:
在這里做一些測試,不保證結果具有通用性,也請大家指正。
void dfs(int i) {int k = 0;cout << "&k = " << &k << endl;cout << "i = " << i << endl;i = i+1;dfs(i); } int main() {int i = 0;dfs(i); }變更dfs(i)參數個數
遞歸次數:32385,第一個k的地址 0x61fdcc,k地址間隔 6410(參數個數為1-4個)
增加參數個數到(5-6個):
遞歸次數:25908,第一個k的地址 0x61fdbc(比上面移動了16),k地址間隔 8010
增加參數個數到(7-8個):
遞歸次數:21589,第一個k的地址 0x61fdac(比上面移動了32),k地址間隔 9610
增加參數個數到(9-10個):
遞歸次數:18505,第一個k的地址 0x61fd8c(比上面移動了64),k地址間隔 11210
增加參數個數到(11個):
遞歸次數:16191,第一個k的地址 0x61fd7c(比上面移動了80),k地址間隔 12810
增加參數個數到(10個,且全部改成&引用):
遞歸次數:18505,第一個k的地址 0x61fd8c(比上面移動了64),k地址間隔 11210
以上均為win1064位操作系統 環境
目前通過結果,可以看見,
- 參數的增多,遞歸層數會減小
- 遞歸中的棧也遵守內存對齊原則
- int 型的 & 對遞歸深度沒有造成影響(win10,64位)
上面程序,
遞歸次數:1436,第一個k的地址 0x61f34c(比上面移動了2688),k地址間隔 144010
將 m1 改成&:
cout << "size of memory1& " << sizeof(&m1) << endl; size of memory1& 8遞歸次數:16181,第一個k的地址 0x61f86c(比上面移動了1376),k地址間隔 12810
- 對于大型 結構數據,采用&能大幅節省空間,遞歸不至于過早爆棧結束。
但是,上面 LeetCode 爆棧就只是把 int&改成int程序就不爆棧了,跟認知的規律是不符合的,還請大佬看看什么原因?
3. C++引用的本質
參考:c++中“引用”的底層實現原理詳解
在讀完上文后,就清楚了,引用會產生一個8字節的變量存儲被引用的變量的地址,所以上面win10的測試結果,有點不可信,可能這就是C++在硬件、操作系統、編譯器不同的情況下結果有差異的情況,采用 linux 進行測試
在linux中測試結果:
傳入2個int: 遞歸次數174522
傳入2個int&:遞歸次數130885
傳入2個double: 遞歸次數130912
傳入2個double&:遞歸次數104668
我想這個數值,已經能夠側面說明上面鏈接文章中提到的引用本質了,C++引用的本質是指針,但是它跟指針又不一樣,C++對指針進行了封裝產生了引用,你在使用引用的時候,傳給你的是它里面指針所指向的內容。
所以對這種內置的變量類型,函數調用的時候,直接使用copy傳入就可以了,還比較省內存(int 4字節,使用 int & 會占用 8字節)
至此,可以解釋上面 LeetCode 那道題,傳入 int & 爆棧了,而改為 int ,題目就AC通過了。
4. 致謝
感謝焦/huaix提出去掉&可以解決問題,開啟了我對這個問題的思考
感謝Thin-k.調試,確認是stack-overflow的問題
感謝CSDN群里的朋友熱心討論和研究,還有論壇朋友akari10032的解答
感謝hitskyer和阿福的答疑
感謝所有在網絡上分享知識的每一個博主!
參考鏈接:
帶你深入理解內存對齊最底層原理
C/C++內存對齊詳解
總結
以上是生活随笔為你收集整理的C++ 内存对齐 及 引用是否真的节省内存的一点思考的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LeetCode 96. 不同的二叉搜索
- 下一篇: LeetCode 991. 坏了的计算器