C 是否最好在函数内部重新声明一个结构,或者每次将其声明为静态并设置为0?

C 是否最好在函数内部重新声明一个结构,或者每次将其声明为静态并设置为0?,c,struct,c99,C,Struct,C99,基本上,如果我有一个结构,如: struct header { char ptr[512]; }; 我有一个这样的函数: void some_function() { struct header header = { 0 }; // do something with struct } 这样做是否真的有利于性能: void some_function() { static struct header header; memset((char *)&header

基本上,如果我有一个结构,如:

struct header {
  char ptr[512];
};
我有一个这样的函数:

void some_function() {
  struct header header = { 0 };

  // do something with struct
}
这样做是否真的有利于性能:

void some_function() {
  static struct header header;

  memset((char *)&header, 0, sizeof(header));
  // do something with struct
}

我知道,如果结构包含指针,memset并不总是有效,因为NULL可能不在地址0x0000处,但在这种情况下,如果这不重要,那么更好的方法是什么?

如果C程序指定了一个实际的静态对象或在堆栈上自动分配的对象,这两段代码的性能几乎相同。可能会有微小的性能差异,这取决于一个属性的寻址方式,也可能会有一些性能差异,这取决于它们相对于其他数据和缓存属性的分配位置。(特别是,自动版本可能具有更好的属性,因为内存不是专门为结构保留的。当其他函数执行时,它将与其他数据共享,而不是
某些函数
,因此它可能更频繁地驻留在缓存中,并导致更少的内存访问。此外,由于t将与其他函数共享,整个程序可能会使用更少的内存,从而提高性能。)

然而,C程序并没有直接指定计算机必须做什么(尽管有些C实现可能是用这种方式实现的,或者有开关来实现的,或者类似的东西)。根据C标准,C程序在抽象机器中指定一个虚拟计算。C编译器的工作是将该计算转换为一个用于实际机器的程序。这样做有很大的自由度

这意味着,如果编译器能够看到并充分分析足够多的源代码,从而看到函数的两个版本的行为相同(就可观察的行为而言),那么它可以将它们转换为相同的代码。(可观察的行为包括输入和输出交互、对易失性对象的访问以及写入文件的数据。)在这种情况下,没有性能差异

如果有什么区别的话,自动版本更容易让编译器进行分析。它知道当函数结束时,自动对象将消失(在抽象机器中)。尽管在这两种情况下,您都会在函数开始时清除对象,因此编译器(假设已在其中内置了有关
memset
的知识)知道每次函数启动时对象都会在这方面重新开始,但编译器编写者还需要担心其他方式的行为可能会有所不同。例如,如果采用静态结构的地址,特别是如果将其传递给任何其他例程,编译器必须担心其中的数据可能会在函数返回后被保留其地址的其他代码使用。相反,对于自动结构,编译器可能表现为在函数返回后从未使用自动对象,因为在抽象机器中,当函数返回时自动对象不再存在。(因此,如果任何其他代码保留了它的地址,那么该地址的使用不是由C标准定义的,编译器也不必为它做任何事情。)

因此,除了在深奥的环境中或者仅仅是内存和缓存行为的偶然情况下,我们通常可以预期自动版本至少和静态版本一样好

一般来说,编写软件来表达您需要的内容,并且只表达您需要的内容。如果一个对象不需要在函数的生命周期之后继续存在,那么将其作为一个自动对象,不要使其成为静态对象

请注意,通常没有必要将所有此类结构归零,因为:

  • 所使用的结构部分可能会用长度或哨兵(例如标记结束的空字符)表示,因此软件不会尝试读取任何后续部分,因此无需对其进行初始化
  • 或者,如果所有结构都将被读取,那么软件可以设计为填充非零部分,然后只将剩余部分归零,而不是首先将整个结构归零

有一种“正确”的方法可以做到这一点的想法假设有一个快速解决方案。在C语言中并非如此。在其他已经构建为具有quick init的语言中,有一种方法可以做到这一点。C中没有。问题是哪个更好,而不是哪个“对”。这是一个公平的问题。如果你的答案是“没关系”,介意把它作为一个答案贴出来吗?这两种方法在实践上几乎没有区别。静态变量通常仅在需要在调用之间保存值时使用。如果您每次都初始化变量,并且没有返回指向它的指针,那么就没有理由将其设置为静态的……除非它非常大并且可能会破坏堆栈。一个问题:您提到的事实是编译器可能会为这两个版本生成类似的代码,并且它们可能具有类似的结果,但是考虑到一个足够大的结构(512字节可能不算大,但是)不是每个自动对象的堆栈分配都需要“大量”的时间吗,与已从静态对象保留内存相比?@AlexGh:512字节对象的堆栈分配通常最多需要一条指令:从堆栈指针减去512。通常它需要零指令,因为编译器仅将其包含在已对函数的整个堆栈帧执行的减法中。