当前位置: 首页 > article >正文

几种编程语言之结构体内存对齐

写在前面

计算机的内存空间是以 byte 来划分,因此我们可以简单的认为不同的数据类型,就占有相应大小的内存空间,但实际此举会为内存的访问带来效率上的问题。实际内存访问是会以一个固定字长来开始访问,而这样就会产生一个问题,有些数据类型的字长会大于或小于这个固定字长。
为了解决这个问题,不同的编译器会对不同的数据类型做一些规定,会分两步走:一个是每个数据类型所占的空间,应该是某个字节数的整数倍;另一个是如果实际类型变量所占的空间达不到规定的大小,就会做填充,使其符合。这个过程就是内存对齐。

内容

可以从 Go 语言里看一个例子:

package main

import (
	"fmt"
	"unsafe"
)

type demo1 struct {
	a int8
	b int16
	c int32
}

type demo2 struct {
	a int8
	c int32
	b int16
}

func main() {
	fmt.Println(unsafe.Sizeof(demo1{})) // 8
	fmt.Println(unsafe.Sizeof(demo2{})) // 12
}

可以看到,两个结构体里的属性其实是一样的,只是它们的顺序有所不同,导致打印出来的 size 也不同。那为什么呢?
以 demo1 为例,第一个变量类型为 int8,所占为 1 个字节,编译器规定为 1 的倍数,而它也是第一个,从下标为0的偏移量开始,所以无需对齐。
第二个变量 int16,规定对齐倍数是2,故需要在下标偏移量为2的地方开始,并且占2个字节,它前面的偏移量1会留空填充。
第三个变量 int32,规定对齐倍数是4,故需要在下标偏移量为4的地方开始,并且占4个字节。
因此 demo1 所占的空间为 1 + 1 + 2 + 4 = 8 个字节。
而 demo2 里,同样第一个变量类型为 int8,所占为 1 个字节,编译器规定为 1 的倍数,而它也是第一个,从下标为0的偏移量开始,所以无需对齐。
第二个变量 int32,规定对齐倍数是4,故需要在下标偏移量为4的地方开始,并且占4个字节。故前面的1、2、3会作为留空填充。
第三个变量 int16,规定对齐倍数是2,故需要在下标偏移量为8的地方开始,并且占2个字节。
因此似乎 demo2 所占的空间为 1 + 3 + 4 + 2 = 10 个字节。

这里就需要引入另外一个东西 unsafe.Alignof:

package main

import (
	"fmt"
	"unsafe"
)

type demo1 struct {
	a int8
	b int16
	c int32
}

type demo2 struct {
	a int8
	c int32
	b int16
}

func main() {

	fmt.Println(unsafe.Alignof(demo1{})) // 4
	fmt.Println(unsafe.Alignof(demo2{})) // 4
}

这个东西叫对齐保证,因为这里我们要处理的是 demo1、demo2 这一整个的特定类型,通过对齐保证,我们可以得到一个数字,这个数字告诉我们,这个特定类型实际所占的空间,将会是这个数字的整数倍。
由于demo1 的对齐保证是4,刚好里面的各个属性的空间+对齐空间为8字节,为4的整数倍。
而 demo2 的对齐保证也是4,但里面各个属性的空间+对齐空间为10字节,不是4的整数倍,故最后一个 int16 类型的变量,需要再填充对齐2个字节,使得实际是12个字节,为4的整数倍。

在C语言里也有类似的例子:

#include <stdio.h>

typedef struct t1{
    char x;
    int y;
    double z;
}T1;

typedef struct t2{
    char x;
    double z;
    int y;
}T2;

int main(int argc, char* argv[])
{
    printf("sizeof(T1) = %lu\n", sizeof(T1)); // 16
    printf("sizeof(T2) = %lu\n", sizeof(T2)); // 24

    return 0;
}

有时候如果我们对一些内存空间要求比较高的地方,可以通过分析内存对齐,调整字段的位置,来作为一种优化的手段。但大多数时候应该是没必要去关注这些的吧。

参考

C语言内存对齐详解
Go语言101-内存布局
Go struct 内存对齐
Size and alignment guarantees


http://www.kler.cn/a/15490.html

相关文章:

  • 如何轻松导出所有 WordPress URL 为纯文本格式
  • 如何使用 XML Schema
  • Visual Studio 2017 快捷键设置-批量注释和批量取消注释
  • 大数据新视界 -- 大数据大厂之 Impala 性能飞跃:分区修剪优化的应用案例(下)(22 / 30)
  • 某某科技笔试题
  • 【MySQL】InnoDB内存结构
  • IDA简单使用
  • 用Python分析周杰伦歌曲并进行数据可视化
  • node项目的建立
  • HTTP 知识点总结
  • MRI k空间概念整理
  • 怎么卸载现有Python?【全方位解决】
  • 1。C语言基础知识回顾
  • 真题详解(数据流图平衡)-软件设计(五十九)
  • 数字IC入门教程
  • DDD系列:一、 Domain Primitive
  • 进程状态以及两种特殊进程
  • Java 网络编程 —— Socket
  • C——Typedef是什么?如何使用?有何便利之处?
  • 下载——安装——使用FinalShell
  • 权威学者、企业CFO荟聚上海国家会计学院,共探「智能会计 价值财务」
  • 30基于非对称纳什谈判的多微网电能共享运行优化策略MATLAB程序
  • 最值得学的编程语言是哪个?
  • 10、Java继承与多态 - 内部内的概念与分类
  • hw xm 的额外symbol汇总
  • 为什么要用虚拟 DOM?