前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++ 内存对齐 及 &引用是否真的节省内存的一点思考

C++ 内存对齐 及 &引用是否真的节省内存的一点思考

作者头像
Michael阿明
发布2020-07-13 16:40:21
9740
发布2020-07-13 16:40:21
举报
文章被收录于专栏:Michael阿明学习之路

1. 内存对齐

通过以下语句,获取变量的占用内存打下:

代码语言:javascript
复制
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,排列顺序不同。

代码语言:javascript
复制
class memory1
{
    int id;
    char a;
    char b;
};
class memory2
{
    char a;
    int id;
    char b;
};
class memory3
{
    char a;
    char b;
    int id;
};

可以看见,他们的占用空间大小是不一样的。

代码语言:javascript
复制
size of memory1 8
size of memory2 12
size of memory3 8

所以上面的memory类的内存对齐是按照4字节进行的,计算机按照顺序分配内存,4字节剩余空间能放下某个类型的,就放进去,放不进去的,新往下找一块4字节的空间放 int

加入#pragma pack(1),可见就是紧密排列了。

代码语言:javascript
复制
size of memory1 6
size of memory2 6
size of memory3 6
代码语言:javascript
复制
#pragma pack(2)
size of memory1 6
size of memory2 8
size of memory3 6

2. 递归中的内存对齐

我在做LeetCode题的时候遇到一个递归爆栈问题

在这里做一些测试,不保证结果具有通用性,也请大家指正。

代码语言:javascript
复制
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位)
代码语言:javascript
复制
class memory1
{
    int id;
    char a[1280];
    char b;
};
int main() {
    int i = 0;
    int j = 0;
    int a = 0, b = 0, c = 0,d=0,e=0,f=0,g=0,h=0,l=0;
    memory1 m1;
    dfs(m1,i, j, a, b,c,d,e,f,g,h);
}
代码语言:javascript
复制
size of memory1 1288

上面程序,

递归次数:1436,第一个k的地址 0x61f34c(比上面移动了2688),k地址间隔 144010

将 m1 改成&

代码语言:javascript
复制
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++内存对齐详解

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/03/24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 内存对齐
  • 2. 递归中的内存对齐
  • 3. C++引用的本质
  • 4. 致谢
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档