这种 malloc 的使用会导致“可选”结构字段吗?

Posted

技术标签:

【中文标题】这种 malloc 的使用会导致“可选”结构字段吗?【英文标题】:Could this use of malloc lead to an "optional" struct field? 【发布时间】:2021-06-16 19:35:27 【问题描述】:

我正在实现一个结构,我需要(在运行时)有一个可选字段。 于是我想到了这个:

//...
#include <stdlib.h>

struct test 
  int x;  // Must be
  int y;  // Optional (Must be the last field..(?))
;

int main(int argc, char **argv) 
    // With the optional field
    struct test *a = malloc(sizeof(*a));
    a->x = 11;
    a->y = 55;
    
    // Without the optional field
    struct test *b = malloc(sizeof(*b) - sizeof(int));
    b->x = 22;
    
    // ...
    
    free(a);
    free(b);
    return 0;

这段代码可以满足我的要求吗? 可能会添加一个位字段来检查是否有可选字段。

另外,如果建议的解决方案有效,如果这是针对多个项目 (> 100000) 的列表实现的,那么这样做以节省内存会更好吗?

【问题讨论】:

@Lundin 是的,因为我正在为一块小板开发,其中一千个未使用的 4 字节会有所不同。 :-) 那你为什么在低端嵌入式系统上使用malloc?这更没有意义。 我相信你可以 malloc 一个较小的结构,并将较小的结构作为第一个成员放在较大的结构中。 @Lundin 这就是我被告知要做的,这是一个研究/研究项目 【参考方案1】:

我认为您提出的解决方案很危险。使用两种不同的结构:

struct test_x 
   int x;
;

struct test_xy 
  int x;
  int y;
;

要么有两个数组,要么存储一个 void * 和一个鉴别器(例如标记指针)。另一个选项是对可选元素使用指针,但 sizeof(int *) 至少在我的盒子上与 sizeof(int) 相同,这样只会使事情变大。

如果所有 y 成员都是可选的,则考虑列布局,或者您可以对数据进行排序,以便所有 xy 元素排在第一位:

struct test_column 
  int *x;
  int *y;
;

struct test_column t = 
  .x = malloc(100000 * sizeof(int)),
  .y = 0

它对你没有帮助,但联合是两个结构共享内存的标准方式,所以每个元素的大小是 max(sizeof(test_xy), sizeof(test_x)) 而不是 sizeof(test_xy) + sizeof(test_x)。

最后,考虑压缩,尤其是使用 test_column 格式时。

【讨论】:

【参考方案2】:

您尝试的内容不符合 C 标准,因为您尝试使用类型为 struct test 的对象,但该对象没有为其分配足够的内存,即使您只是访问字段为其分配了内存。它可能有效,但你不能依赖它。

可以做的是利用灵活的数组成员

struct test 
  int x;
  int y[];
;

在这样的结构中,sizeof(struct test) 不包括最后一个成员。您可以通过为结构分配空间以及所需的最后一个成员的尽可能多的数组元素来使用这样的结构。例如:

struct test *b = malloc(sizeof(*b) + sizeof(int));
b->x = 1;
b->y[0] = 2;

您需要使用数组索引来访问最后一个成员,但这是一种以符合标准的方式执行所需操作的方法。

如果你不想要最后一个成员,你可以这样做:

struct test *b = malloc(sizeof(*b));
b->x = 1;

【讨论】:

这对我来说真的很奇怪,我认为y[] 被认为是一个指针(以至于它也可以用*b-&gt;y 访问),但实际上它并不存在。很好..谢谢你 @Giuseppe 灵活的数组成员是一项特殊功能,只能用作结构的最后一个成员。在其他地方它意味着一个不完整的数组 - 如果是函数声明的一部分,那么这样一个不完整的数组确实会“衰减”为指向第一个元素的指针。【参考方案3】:

这段代码可以满足我的要求吗?

嗯,它可以,但你不能依赖它。不要这样做;这不是编写正确程序的方法。

当您编写b-&gt;x = 22; 时,编译器有权表现得好像在b 中有一个完整的struct test。你可能会想,“我只是将 22 放入成员 x 的字节中”,但编译器可能会使用“存储 8 个字节”指令:

考虑一些将内存组织成八字节组的架构。总线只能读取和写入整个 8 字节块。 由于无法在硬件中写入 4 个字节,将 4 个字节写入内存需要读取 8 个字节,在处理器寄存器中对其进行操作以在其中 4 个字节中插入所需的值,然后将 8 个字节写回内存。 编译器想要优化b-&gt;x = 22;,它知道y还没有被设置,所以允许它有任何值。因此,编译器不会使用低效的四字节写入序列,而是生成一个八字节存储,将 22 放入 b-&gt;x 并将 0 放入 b-&gt;y

然后这会失败,因为编译器刚刚将 0 写入可能用于其他用途的内存,因为它不是您为 b 分配的空间的一部分。

“如果你对编译器撒谎,它就会报仇雪恨。” — 亨利·斯宾塞

【讨论】:

以上是关于这种 malloc 的使用会导致“可选”结构字段吗?的主要内容,如果未能解决你的问题,请参考以下文章

绕过 C++ 中的函数以在目标进程中从 ExtTextOut 复制数据字符串时,malloc 会导致崩溃吗?

结构中的数组和结构的 Malloc

typedef 结构在链接静态库时会导致名称冲突吗?

我的 BigTable 架构会导致热点吗?

C 中的 malloc() 和 free()

求高手。讲解 malloc 函数的用法 在数据结构中多次出现,意思申请内存,可是不懂如何用