✍个人博客:Pandaconda-CSDN博客
📣专栏地址:http://t.csdnimg.cn/fYaBd
📚专栏简介:在这个专栏中,我将会分享 C++ 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪
1. 简要说明 C++ 的内存分区
C++ 中的内存分区,分别是堆、栈、自由存储区、全局/静态存储区、常量存储区和代码区。
栈:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆:就是那些由 malloc 等分配的内存块,和堆是十分相似的,不过是用 free 来结束自己的生命(new 也可以在堆上分配内存)。
自由存储区:就是那些由new
分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new
就要对应一个delete
。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的 C 语言中,全局变量和静态变量又分为初始化的和未初始化的,在 C++ 里面没有这个区分了,它们共同占用同一块内存区,在该区定义的变量若没有初始化,则会被自动初始化,例如 int 型变量自动初始为 0。
常量存储区:这是一块比较特殊的存储区,这里面存放的是常量,不允许修改。
代码区:存放函数体的二进制代码。
2. C++ 中类的数据成员和成员函数内存分布情况
C++ 类是由结构体发展得来的,所以他们的成员变量(C 语言的结构体只有成员变量)的内存分配机制是一样的。下面我们以类来说明问题,如果类的问题通了,结构体也也就没问题啦。类分为成员变量和成员函数,我们先来讨论成员变量。
一个类对象的地址就是类所包含的这一片内存空间的首地址,这个首地址也就对应具体某一个成员变量的地址。(在定义类对象的同时这些成员变量也就被定义了),举个例子:
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
this->age = 23;
}
void printAge()
{
cout << this->age <<endl;
}
~Person(){}
public:
int age;
};
int main()
{
Person p;
cout << "对象地址:"<< &p <<endl;
cout << "age地址:"<< &(p.age) <<endl;
cout << "对象大小:"<< sizeof(p) <<endl;
cout << "age大小:"<< sizeof(p.age) <<endl;
return 0;
}
//输出结果
//对象地址:0x7fffec0f15a8
//age地址:0x7fffec0f15a8
//对象大小:4
//age大小:4
从代码运行结果来看,对象的大小和对象中数据成员的大小是一致的,也就是说,成员函数不占用对象的内存。这是因为所有的函数都是存放在代码区的,不管是全局函数,还是成员函数。
要是成员函数占用类的对象空间,那么将是多么可怕的事情:定义一次类对象就有成员函数占用一段空间。
我们再来补充一下静态成员函数的存放问题:静态成员函数与一般成员函数的唯一区别就是没有 this 指针,因此不能访问非静态数据成员。
就像我前面提到的,所有函数都存放在代码区,静态函数也不例外。所以有人一看到 static 这个单词就主观的认为是存放在全局数据区,那是不对的。
3. 介绍一下内存对齐
-
自然对齐边界:对于基本数据类型,其自然对齐边界通常为其大小。例如,char 型的自然对齐边界为 1 字节, short 为 2 字节, int 和 float 为 4 字节, double 和 64 位指针为 8 字节。具体数值可能因编译器和平台而异。
-
结构体对齐:结构体内部的每个成员都根据其自然对齐边界进行对齐。也就是可能在成员之间插入填充字节。结构体本身的总大小也会根据其最大对齐边界的成员进行对齐(比如结构体成员包含的最长类型为 int 类型,那么整个结构体要按照 4 的倍数对齐),以便在数组中正确对齐。
-
联合体对齐:联合体的对齐边界取决于其最大对齐边界的成员。联合体的大小等于其最大大小的成员,因为联合体的所有成员共享相同的内存空间。
-
编译器指令:可以使用编译器指令 (如 #pragma pack ) 更改默认的对齐规则。这个命令是全局生效的。这可以用于减小数据结构的大小,但可能会降低访问性能。
-
对齐属性:在 C++11 及更高版本中,可以使用 alignas 关键字为数据结构或变量指定对齐要求。这个命令是对某个类型或者对象生效的。例如, alignas(16) int x; 将确保 x 的地址是 16 的倍数。
-
动态内存分配:大多数内存分配函数 (如 malloc 和 new ) 会自动分配足够对齐的内存,以满足任何数据类型的对齐要求。