request-free-img

C语言结构体字节对齐详解:为什么sizeof结果和手动计算不同?

在C语言中,因为字节对齐的原因,会使结构体的实际大小和你计算出来的大小不同。这往往让刚接触C语言的新手感到头大。

一个简单的结构体示例

大家看这是一个简单的结构体,它有3个成员变量a,b,c。

a是一个char占用一个字节,b是一个Int占用4个字节, c是一个short占用2个字节。

struct Example {
char a; // 1 字节
int b; // 4 字节
short c; // 2 字节
};
int main()
{
int size = sizeof(Example);
    std::cout << "size of Example: " << size << std::endl;
return 1;
}

那么我们手动计算出来它一共占用7个字节。

我们来验证一下,咦,它实际上占用了12个字节,这是为什么?

字节对齐的原理

其实这是因为字节对齐导致的。

字节对齐(Memory Alignment) 是一种计算机内存布局优化机制,它规定数据在内存中的存储地址必须满足特定的对齐规则。

字节对齐的目的是为了提高内存访问的效率,减少CPU读取或存储数据时的开销。如果数据没有对齐,CPU 需要额外的指令和周期来读取数据,程序的性能就会下降。

大多数编译器(如 GCC、MSVC)会将结构体的对齐方式设置为最大成员的倍数。

在我们的示例中,最大成员是INT,它占用4个字节,因此其它成员也会按4个字节对齐。

修改为double后的变化

那假如我们把INT改成DOUBLE,因为DOUBLE占用8个字节,那么其它成员变量也会按8个字节对齐, 我们来验证一下。

没错,现在这个结构体占用24个字节。

#include <iostream>
struct Example {
char a; // 1 字节
double b; // 4 字节
short c; // 2 字节
};
int main()
{
int size = sizeof(Example);
    std::cout << "size of Example: " << size << std::endl;
return 1;
}

网络传输中的问题

可能你也发现了,如果是在网络传输中,这种对齐方式是很浪费网络资源的。

使用#pragma pack控制对齐

那怎么控制结构的字节对齐方式呢?

其实使用#pragma pack 就可以用来控制它的字节对齐方式。

例如在我们的示例中,我们希望控制1个字节对齐,我们修改代码加上#pragma pack(1)。

再运行程序,现在这个结构的大小就是7个字节了。

那如果我们控制它2个字节对齐,则char也对齐到2个字节,所以这个结构体的长度就会变成8个字节。

#include <iostream>
#pragma pack(2)
struct Example {
char a; // 1 字节
int b; // 4 字节
short c; // 2 字节
};
int main()
{
int size = sizeof(Example);
    std::cout << "size of Example: " << size << std::endl;
return 1;
}

不同对齐方式的优缺点

1 字节对齐的方式节省内存,非常适合网络数据传输、文件存储等场景。

但是未对齐内存访问可能降低性能,在某些架构的CPU上,未对齐的访问还可能会导致程序崩溃。

而 4 字节对齐提供较好的内存和性能平衡通常是X86架构的默认对齐方式。

所以在跨平台开发时,可以使用编译器的对齐指令确保代码的一致性。

而像JAVA等高级语言就会自动处理好对齐问题。

总结

怎么样,关于字节对齐你现在学会了吗?


更多问题探讨,请关注公众号:程序员角