类和结构体的内存空间占有问题

本文深入解析C++中结构体与类的内存布局,包括空类和结构体的默认大小,结构体成员的偏移量计算,以及类大小受虚函数影响的变化。通过实例代码展示不同类型成员组合下的结构体大小,揭示内存对齐原则及其对性能的影响。

空类和空结构体

代码

class ctest { };
struct stest{ };
int main(int argc, char *argv[])
{
    printf("空类大小=%luB,空结构体大小=%luB.\n", sizeof(stest), sizeof(ctest));
    return 0;
}

结果:

空类大小=1B,空结构体大小=1B.

这里不管是空类还是空的结构体,都会被编译器用一个字节来填充。

结构体大小的度量

在度量结构体大小时,有一个概念——偏移量
偏移量指的是结构体变量中成员的地址和结构体变量地址的差。
结构体大小等于最后一个成员的偏移量加上其大小
由于存储变量时地址对齐的要求,编译器在编译程序时会遵循两条原则:
一、结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)
二、结构体大小必须是所有成员大小的整数倍。

struct stest{
    int a;
    char b;
};
int main(int argc, char *argv[])
{
    printf("结构体(int=%luB,char=%luB)大小=%luB.\n", 
    		sizeof(int), sizeof(char), sizeof(stest));
    return 0;
}

结果:

结构体(int=4B,char=1B)大小=8B.

struct stest{
    int a;
    char b;
    char c;
};
struct stest1{
    char b;
    char c;
    int a;
};
int main(int argc, char *argv[])
{
    printf("结构体(int=%luB,char=%luB, char=%luB)大小=%luB.\n", 
    		sizeof(int), sizeof(char), sizeof(char), sizeof(stest));
    printf("结构体(char=%luB, char=%luB, int=%luB)大小=%luB.\n", 
    		sizeof(char), sizeof(char), sizeof(int), sizeof(stest1));
    return 0;
}

结果:

结构体(int=4B,char=1B, char=1B)大小=8B.
结构体(char=1B, char=1B, int=4B)大小=8B.

struct stest{
    int a;
    char b;
    char c;
    long int d;
};
int main(int argc, char *argv[])
{
    printf("结构体(int=%luB,char=%luB, char=%luB, long int=%luB)大小=%luB.\n", 
    		sizeof(int), sizeof(char), sizeof(char), sizeof(long int), sizeof(stest));
    return 0;
}

结果:

结构体(int=4B,char=1B, char=1B, long int=8B)大小=16B.

struct stest{
    char a;
    struct {
    char b;
    int c;
    }bb;
    int d;
};
int main(int argc, char *argv[])
{
    printf("结构体(char, (char, int), int)大小=%luB.\n", sizeof(stest));
    return 0;
}

结果:

结构体(char, (char, int), int)大小=16B.

对齐的理解

现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始。
但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐
对齐的作用和原因:
各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。
比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。
比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那 么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数 据。显然在读取效率上下降很多。

类大小度量

class ctest {
public:
    ctest();
    ~ctest();
    int func();
private:
    int a;
    char b;
};
int main(int argc, char *argv[])
{
    printf("class大小=%luB.\n", sizeof(ctest));
    return 0;
}

结果:

class大小=8B.

为什么成员函数没有计入大小?哪怕是指针,也应该有大小啊?
哈哈!想想吧,代码应该存储在.text代码区,要是代码都在对象空间里面了,就全乱套了。

但是有一种情况例外,那就是虚函数。

class ctest {
public:
    ctest();
    ~ctest();
    virtual int func();
private:
    int a;
    char b;
};
int main(int argc, char *argv[])
{
    printf("class大小=%luB.\n", sizeof(ctest));
    return 0;
}

结果:

class大小=12B.

因为虚函数会在对象的头部生成虚函数表,那个一个个指针组成的表。

所以,函数声明不占用大小

### 动态内存分配与结构体的可视化解释 在C/C++编程中,动态内存分配允许程序运行时请求特定大小的内存块。这通常通过标准库函数`malloc()`、`calloc()`、`realloc()` `free()` 来完成[^3]。 #### 使用结构体进行动态内存分配的例子 考虑如下定义的一个简单结构体: ```c struct Person { char* name; int age; }; ``` 当需要为这个结构体实例化对象并为其成员变量分配空间时,可以这样做: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> int main(void){ struct Person *person; // 为整个结构体分配内存 person = (struct Person *) malloc(sizeof(struct Person)); if(person != NULL){ // 检查是否成功分配了内存 // 为name字段单独分配足够的字符数组长度 person->name = (char*) malloc(50 * sizeof(char)); strcpy(person->name, "John Doe"); person->age = 28; printf("Name: %s\n", person->name); printf("Age : %d\n", person->age); free(person->name); // 释放字符串占用的空间 free(person); // 释放结构体本身所占有的空间 } } ``` 这段代码展示了如何先为结构体整体申请一块连续的存储区域,再分别为其内部指针型的成员(这里是`name`)开辟额外的空间[^4]。 #### 可视化的理解方式 为了更好地理解展示上述过程,可以通过图形表示法来说明这些概念。假设有一个名为Person的对象被创建,并且已经完成了对其属性值的设定,则可以用下述图表形式表达此状态下的内存布局情况: | 地址 | 数据 | | --- | --- | | 0x7fff...a000 | → 0x6009e0 ("John Doe") | | 0x7fff...a004 | 28 | 这里,“→” 表示指向关系;左侧列出了各部分实际占据的位置地址,右侧则是对应位置上保存的具体数值或内容。对于像`name`这样的指针型成员来说,在表格里会记录它所指向的真实字符串所在的地方而不是直接把字符串放进去。 这种表格式样有助于直观地看到不同组件之间的关联以及它们在整个计算机系统内的相对定位状况。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值