草庐IT

memory - 如果字段顺序不同,则结构具有不同的大小

coder 2023-06-27 原文

package main

import (
    "fmt"
    "unsafe"
)

type A struct {
    a bool
    b int64
    c int
}

type B struct {
    b int64
    a bool
    c int
}

type C struct {
}

func main() {
    // output 24
    fmt.Println(unsafe.Sizeof(A{}))

    // output 16
    fmt.Println(unsafe.Sizeof(B{}))

    // output 0
    fmt.Println(unsafe.Sizeof(C{}))
}
  1. Struct AB 具有相同的字段,但如果以不同的顺序指定,它们会导致不同的大小。为什么?

  2. C 结构的大小为零。系统为 a := C{} 分配了多少内存?

谢谢。

最佳答案

1。结构尺寸

长话短说; (总结): 如果您对字段重新排序,将使用不同的隐式填充,并且隐式填充计入 struct 的大小。 .

请注意,结果取决于目标架构;您发布的结果适用于 GOARCH=386 ,但是当GOARCH=amd64 , 两者的尺寸 A{}B{}将是 24 个字节。

struct的字段地址必须对齐,int64类型的字段地址必须是 8 字节的倍数。 Spec: Package unsafe :

Computer architectures may require memory addresses to be aligned; that is, for addresses of a variable to be a multiple of a factor, the variable's type's alignment. The function Alignof takes an expression denoting a variable of any type and returns the alignment of the (type of the) variable in bytes.

对齐int64是8个字节:

fmt.Println(unsafe.Alignof((int64(0)))) // Prints 8

所以在 A 的情况下因为第一个字段是 bool , A.a 之后有一个 7 字节的隐式填充这样A.b这是类型 int64可以从 8 的倍数的地址开始。这(正好需要 7 字节填充)保证为 struct本身与 8 的倍数的地址对齐,因为这是其所有字段中最大的大小。请参阅:Spec: Size alignment guarantees:

For a variable x of struct type: unsafe.Alignof(x) is the largest of all the values unsafe.Alignof(x.f) for each field f of x, but at least 1.

如果是B (如果 GOARCH=386 是你的情况)在 B.a 之后只会有一个 3 字节的隐式填充。类型字段 bool因为这个字段后面跟着一个 int 类型的字段(大小为 4 个字节)而不是 int64 .

对齐int如果 GOARCH=386 是 4 个字节, 如果 GOARCH=amd64 则为 8 个字节:

fmt.Println(unsafe.Alignof((int(0))))   // Prints 4 if GOARCH=386, and 8 if GOARCH=amd64

使用 unsafe.Offsetof() 找出字段的偏移量:

// output 24
a := A{}
fmt.Println(unsafe.Sizeof(a),
    unsafe.Offsetof(a.a), unsafe.Offsetof(a.b), unsafe.Offsetof(a.c))

// output 16
b := B{}
fmt.Println(unsafe.Sizeof(b),
    unsafe.Offsetof(b.b), unsafe.Offsetof(b.a), unsafe.Offsetof(b.c))

// output 0
fmt.Println(unsafe.Sizeof(C{}))

var i int
fmt.Println(unsafe.Sizeof(i))

输出如果GOARCH=386 (在 Go Playground 上试试):

24 0 8 16
16 0 8 12
0
4

输出如果GOARCH=amd64 :

24 0 8 16
24 0 8 16
0
8

2。零尺寸值

Spec: Size alignment guarantees:

A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.

所以规范只是提示使用相同的内存地址,但这不是必需的。但是当前的实现遵循它。也就是说,不会为大小为零的类型的值分配内存,这包括空结构 struct{}和零长度数组,例如[0]int ,或元素大小为零(且具有任意长度)的数组。

看这个例子:

a := struct{}{}
b := struct{}{}
c := [0]int{}
d := [3]struct{}{}

fmt.Printf("%p %p %p %p %p", &a, &b, &c, &d, &d[2])

输出(在 Go Playground 上尝试):所有地址都相同。

0x21cd7c 0x21cd7c 0x21cd7c 0x21cd7c 0x21cd7c

有关有趣且相关的主题,请阅读:Dave Cheney: Padding is hard

关于memory - 如果字段顺序不同,则结构具有不同的大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34219232/

有关memory - 如果字段顺序不同,则结构具有不同的大小的更多相关文章

  1. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  2. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  3. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  4. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  5. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  6. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  7. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  8. ruby - Chef 执行非顺序配方 - 2

    我遵循了教程http://gettingstartedwithchef.com/,第1章。我的运行list是"run_list":["recipe[apt]","recipe[phpap]"]我的phpapRecipe默认Recipeinclude_recipe"apache2"include_recipe"build-essential"include_recipe"openssl"include_recipe"mysql::client"include_recipe"mysql::server"include_recipe"php"include_recipe"php::modul

  9. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  10. ruby-on-rails - 在 Rails 和 ActiveRecord 中查询时忽略某些字段 - 2

    我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr

随机推荐