简体   繁体   English

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

[英]What is the correct way to initialize a very large struct?

In our code we used to have something like this:在我们的代码中,我们曾经有这样的事情:

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

This used to work great, and then we upgraded versions of GCC and suddenly started seeing stack overflows.这曾经很好用,然后我们升级了 GCC 的版本,突然开始看到堆栈溢出。 Looking at the assembly, the old GCC code (2.x) was basically doing this:查看程序集,旧的 GCC 代码(2.x)基本上是这样做的:

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

The new GCC (3.4.x) was doing this新的 GCC (3.4.x) 就是这样做的

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

After reviewing the C99 spec, I could see why;在查看了 C99 规范后,我明白了原因; C99 basically requires that anonymous structures exist on the stack. C99 基本上要求堆栈上存在匿名结构。 It's a good concept, but this structure was 4 Megabytes large, and only ever intended to exist on heap!这是一个很好的概念,但是这个结构有 4 兆字节大,并且只打算存在于堆上!

We've resorted to making our own 'initialize' function that explicitly sets the members, but that's ugly and a maintenance headache.我们已经使用我们自己的“初始化”函数来显式设置成员,但这很丑陋,而且维护起来很头疼。 I don't consider memset a proper solution, because I can't know that a bit-value of 0 is an appropriate zero value for the type ( nit-picking, I know, but there you are; I don't mind that the compiler does it, because it can know )我不认为 memset 是一个合适的解决方案,因为我不知道 0 的位值是该类型的适当零值(我知道挑剔,但你在那里;我不介意编译器这样做,因为它可以知道)

What is the "correct", or at least best, way to initialize a large structure like this?初始化像这样的大型结构的“正确”或至少是最好的方法是什么?

To furthur clarify why I think memset isn't a solution: The rules of initialization of members not explicitly initialized are the same as static initialization, and are as follows: - If it has pointer type, it is initialized to a null pointer;进一步澄清为什么我认为 memset 不是一个解决方案:未显式初始化的成员的初始化规则与静态初始化相同,如下: - 如果它有指针类型,则将其初始化为空指针; - If it has arithmetic type, it is initialized to ( positive or unsigned ) zero; - 如果是算术类型,则初始化为(正数或无符号数)零; ... ...

'memset' will set the memory to bit-pattern zero, which isn't necessarily the same thing. 'memset' 会将内存设置为位模式零,这不一定是同一件事。 Imagine a system that doesn't use IEEE floating point numbers.想象一个不使用 IEEE 浮点数的系统。 Unusual, but supported by C. The representation of 0.0 doesn't have to mean "all-bits zero", it could be anything convenient to the processor.不寻常,但由 C 支持。 0.0 的表示不一定意味着“所有位为零”,它可以是处理器方便的任何东西。

memset is the way to go. memset 是要走的路。 You do not have many alternatives.你没有很多选择。

Do something like:做类似的事情:

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

So that you only have to:所以你只需要:

InitStruct(st, BigStruct);

And then use st as usual...然后像往常一样使用 st ...

I do not get how "0" is not a valid "0" type for a struct.我不明白“0”如何不是结构的有效“0”类型。 The only way to "mass initialize" a struct is to set all of its memory to a value; “批量初始化”一个结构体的唯一方法是将其所有内存设置为一个值; otherwise you would have to make extra logic to tell it to use a specific bit pattern per member.否则,您将不得不制定额外的逻辑来告诉它对每个成员使用特定的位模式。 The best "generic" bit pattern to use is 0.要使用的最佳“通用”位模式是 0。

Besides - this is the same logic that you used when doing此外 - 这与您在执行时使用的逻辑相同

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

Therefore I don't get your reluctance to use it :)因此,我不会让您不愿意使用它:)

The first comment to this post made me do some research before I called him and idiot and I found this:这篇文章的第一个评论让我在打电话给他和白痴​​之前做了一些研究,我发现了这个:

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

Very interesting;非常有趣; if I could vote-up a comment I would :)如果我可以对评论进行投票,我会:)

That being said - your only option if you want to target archaic architectures with non-0 null values is still to do manual initialization to certain members.话虽如此 - 如果您想针对具有非 0 空值的古老架构,您唯一的选择仍然是对某些成员进行手动初始化。

Thanks Thomas Padron-McCarthy!感谢托马斯·帕德隆-麦卡锡! I learned something new today :)我今天学到了新东西:)

If you don't want to use memset, you could always declare a static copy of your struct and use memcpy, which will give similar performance.如果您不想使用 memset,您始终可以声明结构的静态副本并使用 memcpy,这将提供类似的性能。 This will add 4 megs to your program but is probably better than setting individual elements.这将为您的程序增加 4 兆字节,但可能比设置单个元素更好。

That said, if GCC was using memset, and it was good enough previously, I would suggest it is good enough now.也就是说,如果 GCC 使用的是 memset,并且以前它已经足够好,我建议它现在已经足够好。

Private initialization function is not ugly rather a good OO way to initialize objects (structs).私有初始化函数并不难看,而是一种初始化对象(结构)的好 OO 方式。 I assume that your structure is not 4MB of pointers, so i would assume that the solution should be like this:我假设你的结构不是 4MB 的指针,所以我假设解决方案应该是这样的:

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

From other hand our code is running on more then 20 embedded operating systems and large number of different hardwares, never meet any problem with just memset of the struct.另一方面,我们的代码运行在 20 多个嵌入式操作系统和大量不同的硬件上,仅使用结构的 memset 就不会遇到任何问题。

As others have said, memset is the way to go.正如其他人所说, memset 是要走的路。 However, don't use memset on C++ objects, particularly those with virtual methods.但是,不要在 C++ 对象上使用 memset,尤其是那些具有虚方法的对象。 The sizeof( foo ) will include the table of virtual function pointers, and doing a memset on that will cause serious grief. sizeof( foo )将包含虚函数指针表,并且在其上执行 memset 将导致严重的悲痛。

If memset doesn't solve the problem by itself, simply do a memset and then initialize any members that should be non-zero (ie your non-IEEE floating point values).如果 memset 本身不能解决问题,只需执行 memset,然后初始化任何应该为非零的成员(即您的非 IEEE 浮点值)。

Many talk about memset without talking about calloc .许多人谈论 memset 而不谈论calloc I would rather use calloc which was designed for this usecase (please comment if I am wrong):我宁愿使用专为此用例设计的 calloc(如果我错了,请发表评论):

The calloc() function allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allocated memory. calloc() 函数为每个大小为字节的 nmemb 元素数组分配内存,并返回指向已分配内存的指针。 The memory is set to zero.内存设置为零。 If nmemb or size is 0, then calloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().如果 nmemb 或 size 为 0,则 calloc() 返回 NULL,或稍后可以成功传递给 free() 的唯一指针值。

Example:例子:

#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;
}

hmm - first of all making an init function and setting each member explicitly IS THE RIGHT THING - it,s the way constructors in OO languages work.嗯 - 首先创建一个 init 函数并明确设置每个成员是正确的 - 这是面向对象语言中构造函数的工作方式。

and second - does anyone know a hardware that implements non IEEE floating point numbers ?其次 - 有谁知道实现非 IEEE 浮点数的硬件? - perhaps Commodore 64 or somethig ;-) - 可能是 Commodore 64 之类的 ;-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM