使用 C 结构成员的连续内存

2023-12-02

在将其标记为重复之前,请先阅读问题。

所以这可能是一个潜在的非常愚蠢的问题,但它困扰着我。我从阅读以及许多其他问题中知道,由于编译器添加的填充,C 结构中的字段不能保证是连续的。例如,根据C标准:

13/在结构体对象中,非位域成员和位域所在的单元的地址按声明顺序递增。指向结构对象的指针经过适当转换后,指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。结构对象内可能有未命名的填充,但不是在其开头。

我正在写一个类似于unix的程序readelf and nm只是为了好玩,它涉及到处理文件中特定偏移处的字节以读取某些值的大量工作。例如,目标文件的前 62 个字节包含“文件头”。文件头的字节 0x00-0x04 编码一个 int,而 0x20-0x28 编码一个指针等。但是,我注意到在 readelf.c 的原始实现中,程序员做了这样的事情:

首先,它们声明一个结构体(我们称之为 ELF_H),其中的字段与文件头中的内容相对应(即第一个字段是一个 int,就像文件头中的前 4 个字节一样,第二个字段是一个 char,因为字节 0x04 elf 标头中的 -0x05 编码字符等)。然后他们所做的就是将整个 elf 文件复制到内存中,并将指向该内存开头的指针键入 ELF_H 类型。就像是:

FILE *file = fopen('filename', rb);
void *start_of_file = malloc(/* size_of_file */);
fread(start_of_file, 1, /* size_of_file */,file);  // copies entire file into memory
ELF_H hdr = *(ELF_H) start_of_file;               // type case pointer to be of type struct and dereference

完成此操作后,只需使用结构体的成员变量访问标头的每个部分即可。因此,他们不是使用指针算术来获取字节 0x04 处的内容,而是只执行 hdr.member2 (在结构中,它是第二个成员,后面跟着第一个 int 成员)。

如果结构体中的字段不能保证连续,这意味着如何工作?

我能找到的最接近的答案是here但在该示例中,结构体的成员具有相同类型。在ELF_H中,它们是不同的类型。

先感谢您 :)


如果文件中的数据是从正在读取的形式的填充结构写入的,则填充是无关紧要的;该文件与内存表示一样包含填充。

确实,该标准并不是特别严格,编译器可以在 ELF 读取器结构中插入随机填充,而编写 ELF 的工具并不匹配。但实际上,“未命名填充”是为了对齐目的,所有主要编译器都有可预测的行为;他们通过填充来对齐字段以匹配其类型。所以int字段(在具有四字节的系统上int) 如果前一个字段未在四字节边界上结束,则前面有 1-3 个填充字节,char字段没有填充等。在这种情况下,我所知道的编译器都不会在前导之间插入填充int字段和以下char[2], 因为char无论如何都不需要对齐。

也可以使用非标准编译器扩展来防止填充来对齐结构中的字段,但如果您的结构定义永远不会有未对齐的字段,则没有必要(因为您总是将较小的字段放在较大的字段后面,或者因为您总是将较小的字段组合在一起以保持后续较大字段的对齐要求)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 C 结构成员的连续内存 的相关文章

随机推荐