初始化一个非常大的结构的正确方法是啥?

Posted

技术标签:

【中文标题】初始化一个非常大的结构的正确方法是啥?【英文标题】:What is the correct way to initialize a very large struct?初始化一个非常大的结构的正确方法是什么? 【发布时间】:2010-09-15 16:45:12 【问题描述】:

在我们的代码中,我们曾经有这样的东西:

   *(controller->bigstruct) = ( struct bigstruct ) 0 ;

这曾经很好用,然后我们升级了 GCC 版本,突然开始看到堆栈溢出。查看程序集,旧的 GCC 代码(2.x)基本上是这样做的:

memset(controller->bigstruct, 0, sizeof(struct bigstruct));

新的 GCC (3.4.x) 正在这样做

   struct bigstruct temp =  0 ;
   controller->bigstruct = temp;

查看 C99 规范后,我明白了原因; C99 基本上要求堆栈上存在匿名结构。这是一个很好的概念,但这个结构有 4 MB 大,而且只打算存在于堆上!

我们采用了我们自己的“初始化”函数来显式设置成员,但这很丑陋,而且维护起来很头疼。我不认为 memset 是一个合适的解决方案,因为我不知道 0 的位值是该类型的适当零值(挑剔,我知道,但你在那里;我不介意编译器会这样做,因为它可以知道)

初始化这样一个大型结构的“正确”或至少是最好的方法是什么?

进一步澄清为什么我认为 memset 不是解决方案:未显式初始化的成员的初始化规则与静态初始化相同,如下所示: - 如果是指针类型,则初始化为空指针; - 如果它具有算术类型,则将其初始化为(正或无符号)零; ...

'memset' 会将内存设置为位模式零,这不一定是同一件事。想象一个不使用 IEEE 浮点数的系统。不寻常,但受 C 语言支持。0.0 的表示不一定意味着“所有位为零”,它可以是任何方便处理器的东西。

【问题讨论】:

如果我可能会问,你的结构中究竟是什么,它有一个占用 4MB 的定义? :// 什么样的平台不支持 IEEE 浮点并让你分配一个 4MB 的结构? 使用静态怎么样?静态结构 bigstruct Zero_bigstruct = 0 ;和 *(controller->bigstruct) = Zero_bigstruct; ? 【参考方案1】:

很多人谈论 memset 而没有谈论calloc。我宁愿使用专为此用例设计的 calloc(如果我错了请评论):

calloc() 函数为每个大小字节的 nmemb 元素数组分配内存,并返回一个指向已分配内存的指针。内存设置为零。如果 nmemb 或 size 为 0,则 calloc() 返回 NULL,或稍后可以成功传递给 free() 的唯一指针值。

例子:

#include <stdlib.h> // calloc header
#include <stdio.h> // printf header

void    *init_heap_array(int elem_nb, int elem_size) 
    void *ptr;

    if (!(ptr = calloc(elem_nb, elem_size)))
        return NULL;

    return ptr;


void    set_int_value_at_index(int *ptr, int value, int i) 
    ptr[i] = value;


void    print_int_array_until(int *ptr, const int until) 
    for (int i = 0; i < until; i++)
        printf("%02d ", ptr[i]);
    putchar('\n');


int     main(void) 
    const int array_len = 300000;
    int *n;

    if (!(n = init_heap_array(array_len, sizeof(int))))
        return 1;    

    print_int_array_until(n, 5);
    set_int_value_at_index(n, 42, 1);
    print_int_array_until(n, 5);

    return 0;

【讨论】:

【参考方案2】:

memset 是要走的路。你没有很多选择。

执行以下操作:

#define InitStruct(var, type) type var; memset(&var, 0, sizeof(type))

所以你只需要:

InitStruct(st, BigStruct);

然后像往常一样使用 st...

我不明白“0”如何不是结构的有效“0”类型。 “大规模初始化”结构的唯一方法是将其所有内存设置为一个值;否则,您将不得不制定额外的逻辑来告诉它为每个成员使用特定的位模式。最好使用的“通用”位模式是 0。

此外 - 这与您在执行时使用的逻辑相同

*(controller->bigstruct) = *( struct bigstruct ) 0 ;

因此我不明白你不愿意使用它:)

这篇文章的第一条评论让我在称他和白痴之前做了一些研究,我发现了这个:

http://www.lysator.liu.se/c/c-faq/c-1.html

非常有趣;如果我可以对评论进行投票,我会:)

话虽如此 - 如果您想针对具有非 0 空值的古老架构,您唯一的选择仍然是对某些成员进行手动初始化。

感谢托马斯·帕德隆-麦卡锡!我今天学到了一些新东西:)

【讨论】:

源代码中的 0 不一定转换为全零位模式。例如,指针上下文中源代码中的 0 将被解释为 NULL 指针,但 NULL 指针不需要表示为全零位。 或者 0 可能是某个索引的有效值,-1 表示“未找到”的初始值。这就是 C++ 有 ctor 的原因。 @dmckee:通过将声明包装在新范围内,这不会破坏实际声明新变量的目的吗? 而且,在单行语句的情况下,无论如何声明一个 var 是没有意义的——这意味着你将得到的编译错误只会提醒你你做错了什么:)【参考方案3】:

私有初始化函数并不丑陋,而是一种很好的 OO 方法来初始化对象(结构)。我假设你的结构不是 4MB 的指针,所以我假设解决方案应该是这样的:

void init_big_struct(struct bigstruct *s)  
  
    memset(s, 0, sizeof(struct bigstruct));  
    s->some_pointer = NULL; // Multiply this as needed  

另一方面,我们的代码在超过 20 个嵌入式操作系统和大量不同的硬件上运行,仅使用结构的 memset 就不会遇到任何问题。

【讨论】:

要缩进代码,只需添加更多空格。第一行、第二行和最后一行应以 4 个空格开头,其他两行应以 8 个空格开头。【参考方案4】:

正如其他人所说,memset 是要走的路。但是,不要在 C++ 对象上使用 memset,尤其是那些具有虚拟方法的对象。 sizeof( foo ) 将包含虚函数指针表,并且对其执行 memset 将导致严重的问题。

如果 memset 不能自行解决问题,只需执行一个 memset 并然后初始化任何应该为非零的成员(即您的非 IEEE 浮点值)。

【讨论】:

【参考方案5】:

hmm - 首先创建一个 init 函数并明确设置每个成员是正确的 - 这就是 OO 语言中构造函数的工作方式。

第二 - 有人知道实现非 IEEE 浮点数的硬件吗? - 也许是 Commodore 64 或类似的 ;-)

【讨论】:

dec alpha 处理器。 dec vax 处理器。检查术语 DFLOAT 许多微控制器编译器使用类似于 IEEE-754 的格式,但不一样。一个常见的例子是使用 8 位指数和 16 位尾数的 3 字节格式。这种事情没有 IEEE 标准,但是这种类型可以比标准的 16 位“半精度”格式更精确和更快地使用 [顺便说一句,出于 SIMD 的目的,我认为是 21 位格式可能会有所帮助——每个 64 位双字存储 3 个值]。【参考方案6】:

如果您不想使用 memset,您始终可以声明结构的静态副本并使用 memcpy,这将提供类似的性能。这将为您的程序增加 4 兆,但可能比设置单个元素更好。

也就是说,如果 GCC 使用 memset,并且之前已经足够好,我建议它现在已经足够好了。

【讨论】:

作为 0 结构本质上是一个零字节序列(除非你在一些非常疯狂的平台上),这不会给程序增加 4M。零初始化的全局对象转到不同的部分并且不放在程序映像中 - 如果整个部分全为零,那有什么意义呢?此外,这个静态结构在运行时甚至不会使用任何内存,因为操作系统的 VM 子系统会将整个内存范围映射到具有写时复制语义的单个系统范围的零页。

以上是关于初始化一个非常大的结构的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

将初始化代码添加到 Spring Boot 应用程序的正确方法是啥?

无论对象是从 nib 加载还是以编程方式创建,执行初始化代码的正确方法是啥?

在c中初始化双指针的正确方法是啥

Vue.js 3 初始化我的数字变量的正确方法是啥? [复制]

在单击按钮时重置/重新初始化/重新启动 UIViewController 视图的最简单(正确)方法是啥?

在 C++ 中初始化非常大的向量