让我们考虑一个简单的 c 结构:
struct foo { const char * str; unsigned char flag; uint64_t len; };
假设此代码作为在 64 位计算机上执行的程序的一部分运行:您期望 sizeof(struct foo) 的结果是什么?
大多数从未纠结于结构大小和优化的人会猜测它应该是 17...
...但是已经24了!这是为什么?
出现这种行为的原因是编译器优化结构布局以提高速度,并且现代规范认为对齐内存访问是访问数据的最快方式。
这意味着,根据字段和 cpu 的类型,您的数据将具有一定的对齐方式,并且将被定位为遵守该对齐方式(或者,使得字段地址 % 字段对齐 == 0)。
在前面的示例中,指针和 64 位字段在 64 位机器上是 8B 对齐的,这意味着,为了强制结构中的所有内容都对齐的布局,编译器将生成一些flag 和 len 字段之间的填充:
现在让我们考虑另一个示例,其中定义了这样的结构,与之前相同的机器上:
struct bar { const char * str; short s1; int i1 short s2; int i2; };
我们如何计算它的大小?
共有三个规则:
快速回顾 64 位机器的基本类型对齐和大小:
type | size | alignment |
---|---|---|
char | 1 | 1 |
short | 2 | 2 |
int | 4 | 4 |
long | 8 | 8 |
float | 4 | 4 |
double | 8 | 8 |
pointers | 8 | 8 |
还请记住:
您可以使用超级有用的 sizeof 和 _Alignof 运算符来获取自定义类型的此信息。请注意,_Alignof 从 C11 开始可用,从 C23 开始称为alignof。从 C 11 开始,根据我对 C 的理解,它始终是alignof。
有关此主题的更多信息,有关该主题的圣经是“失落的结构包装艺术”,我从中学到了几乎所有有关这方面的知识,以及大量的实践和实践经验。
这个话题在工作中经常出现,当在队列中连续发送大量数据时,节省字节非常重要。
为了让我的生活更轻松,我编写了一个工具,可以参考源文件或代码片段,对作为输入传递的类型生成一些统计信息,它称为 stropt(结构优化器)。
GitHub 上的 Abathargh/stropt
如果您有本地 go 安装,您可以直接构建或安装应用程序:
struct foo { const char * str; unsigned char flag; uint64_t len; };
github 发布页面中还提供了已编译的操作系统/架构组合列表的二进制文件:
stropt 二进制文件
您可以通过将源代码作为字符串进行分析来使用 stropt:
struct bar { const char * str; short s1; int i1 short s2; int i2; };
或者您可以传递包含定义的文件:
git clone https://github.com/Abathargh/stropt go build // or, if you want to install this directly go install github.com/Abathargh/stropt
该工具还可以通过使用 -optimize 标志为您的类型提供可能的优化,并且它知道结构本身的字段:
请注意,详细标志用于显示内部结构(和联合)及其字段对齐和大小。
这个工具是用 go 语言编写的,使用出色的modernc.org/cc C 编译器前端来解析 C 代码,并使用 charmbracelet 的 lipgloss 作为 UI。
我是为自己写的,但我很高兴公开分享;我想将其打造成一个网络应用程序,以便于直接在浏览器中使用,所以这可能是我接下来要做的事情!
以上是优化 C 结构布局的详细内容。更多信息请关注PHP中文网其他相关文章!