结构体与共用体字节数计算

版权声明:本文为博主原创文章,转载请注明出处-- https://blog.csdn.net/qq_38790716/article/details/84931994

写在前面

以前所了解的字节数计算,都想当然的以为将所有数据类型所占的字节数叠加即成了总的字节数,直到今天遇到了这一类问题才好好研究了一下,发现这里面竟有很多我不曾掌握的知识点,那么现在就来学习一下如何计算结构体与共用体、两者相互嵌套的字节数计算

内存对齐

首先在计算这两者的字节数之前,需要了解有关内存对齐,不然做题时容易不知所措

内存对齐的原因

1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常(即各个平台有各个平台自己的特性
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问

关于2,可以举个简单的例子:

struct test {
	char a;
	int b;
	char c;
};

如这个结构体,若无内存对齐进制,则可能存在的情况就是下面这种,b在内存中被分为两段存储,由此得出我们访问b的时候处理器需要做2次操作,而使用内存对齐机制则可以很好的避免这一点
在这里插入图片描述

内存对齐的规则

在没有#pragma pack宏(可通过该宏修改内存对齐的字节数)的情况下,请牢记以下3条原则:

1:数据成员对齐规则:结构体(struct)(或联合体(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储

2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储)

3:收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍.不足的要补齐

参考自猫已经找不回的博客

计算字节数

  1. union的字节数计算:
#include <iostream>
using namespace std;
union A {
        int a[5];
        char b;
        double c;
};
int main()
{       
        cout << sizeof(A) << endl;
        return 0;
}

得出以下结果(使用G++ 7.3.0版本,平台ubuntu 18.04LTS):
在这里插入图片描述

现在来分析一下该结果:按照以前的脑回路肯定会这样想,共用体共用一个内存地址,所以寻找最大的,即int a[5],得出5*4 = 20,但答案却是24;然后这个时候就要考虑内存对齐这一规则了,结构体中有double,为8bytes,当采用20bytes时,只能存放两个半double,所以考虑到这一点就自动对齐为8的倍数同时满足其他变量,即为24bytes

  1. struct的字节数计算
#include <iostream>
using namespace std;
struct B {
        char c;
        double b;
        int c;
} test_struct_b;
int main()
{
        cout << sizeof(test_struct_b) << endl;
        return 0;
}

得出以下结果(使用G++ 7.3.0版本,平台ubuntu 18.04LTS):
在这里插入图片描述

分析:struct与union不同,其中每个数据成员在内存中的位置不同,所以需要将其叠加计算总字节数,然后考虑内存对齐规则
根据内存对齐规则1(结构体(struct)(或联合体(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储
char a放在偏移量(offset)为0的地方,大小为1byte,而double为8bytes,需要从8的整数倍开始,则double b存放于[8]~[15]的位置,接下来int为4bytes,需要从4的倍数开始,则int c存放于[16]~[19]的位置,即得20bytes,然后与上题一致,需要再次对齐,得出24bytes

  1. 混合结构体字节数的计算
#include <iostream>
using namespace std;
typedef union {
	long i;
	int k[5];
	char c;
} UDATE;
struct data {
	int cat;
	UDATE cow;
	double dog;
} too;
UDATE temp;
int main()
{
	cout << sizeof(struct data) + sizeof(temp) << endl;
	return 0;
}

得出以下结果(使用G++ 7.3.0版本,平台ubuntu 18.04LTS):
在这里插入图片描述

分析:首先我们来看temp,temp是个union,跟我们看第一题是一样的思路,很容易得出sizeof(temp) = 24,而union 本身最大为20,所以来分析struct的大小;[以下仅为个人想法,如有更好想法请在评论区留言,可以一同探讨]根据内存对齐规则,int cat为4bytes,而union应从它的倍数开始,即从20开始[20]~[39],接下来的double dog从8的倍数开始,即从8开始,[8]~[15],然后取其之和并进行填充,得出sizeof(struct data) = 40,所以能够得出sizeof(struct data) + sizeof(temp) = 40 + 24 = 64

--------------------------another answer --------------------------------------
这个答案是在stack overflow上我问的关于这道题,然后某个大佬回复我的:

According to the C++ standard:

The size of a union is sufficient to contain the largest of its data members. Each data member is allocated as if it were the sole member of a struct.
So the memory layout of the union is the same as if you had a long there, an array of 5 ints there, or a char there, but spaced to the largest of these (the array of ints). I presume you’re using GCC, and IIRC GCC sets a size of 32-bits for int even on a 64-bit architecture. UDATE would therefore have a size of 20-bytes. You’d naively expect, therefore, sizeof(struct data) + sizeof(temp) to return 52. The fact that you’re apparently getting 64 is probably because GCC is aligning things to 64-bit boundaries and thus taking 24-bytes for UDATE, and inserting a 4-byte spacer between cat and cow in your data struct.

Note that the standard makes no guarantees made about type-punning - that is the frequently used low level technique where the union is written using one method and read using another to, for example, access the bytes that make up an integer - and claims that a union may only be accessed using the same entries that it was written using (baring an exception for a union of POD structs that share common initial members). However, in practice every compiler I’ve ever used will read the memory in the manner you’d expect if you’d simply cast a pointer from one type to another in the union using a reinterpret or C-style cast.

大概意思就是说在GCC中这个struct data中的数据字节数为int的32bytes(对于这个32bytes他解释的我不是很认同),然后加上temp的字节数24bytes,等于52bytes,然后再补齐为64(这个也不是很理解,怎么在两个加完之后再补齐)

-------------------------------------------------get--------------------------------------------------

基本了解了struct与union的字节数计算,虽然还是有些不能理解,但还是获益良多!!!

猜你喜欢

转载自blog.csdn.net/qq_38790716/article/details/84931994