重新声明函数内部的结构还是将其声明为静态并每次都设置为0更好?

时间:2019-11-27 17:23:51

标签: 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, 0, sizeof(header));
  // do something with struct
}

我知道如果结构包含指针,则memset并不总是有效,因为NULL可能不会位于地址0x0000上,但是对于这种情况,这无关紧要,有什么更好的方法呢?

1 个答案:

答案 0 :(得分:0)

如果C程序指定了实际的静态对象或自动在堆栈上分配的对象,则这两段代码的性能几乎相同。基于如何解决一个问题,可能会有微小的性能差异,或者基于关于其他数据和缓存属性的分配位置,可能存在一些性能差异。 (特别是自动版本可能具有更好的属性,因为内存不是专门为结构保留的。在执行其他功能而不是some_function时,它将与其他数据共享,因此它可能更多地驻留在缓存中通常,这样做会减少对内存的访问。此外,由于它会与其他功能共享,因此整个程序总体上可能会使用较少的内存,从而提高了性能。)

但是,C程序并没有直接指定计算机必须执行的操作(尽管某些C实现可能以这种方式实现,或者有开关来实现它,或者接近它)。根据C标准,C程序在抽象机中指定虚拟计算。 C编译器的工作是将计算结果转换为真实机器的程序。这样做的自由很大。

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

如果有的话,编译器更容易分析自动版本。它知道函数结束时自动对象将消失(在抽象机中)。尽管在两种情况下都在函数开始时清除了对象,但是假设编译器已经内置了memset的知识,则每次在函数启动时,编译器都会在这方面重新知道对象,行为可能与编译器编写者必须担心的其他方式有所不同。例如,如果采用静态结构的地址,尤其是将其传递给任何其他例程,则编译器必须考虑到,函数返回后,其中保留数据的其他代码可能会使用其中的数据。地址。相反,对于自动结构,编译器的行为似乎就像在函数返回后从未​​使用过自动对象一样,因为在抽象机中,当函数返回时它不再存在。 (因此,如果其他任何代码确实保留了其地址,则C标准未定义该地址的使用,并且编译器无需为此做任何事情。)

因此,除了神秘的情况或仅发生内存和缓存行为外,我们通常可以期望自动版本至少与静态版本一样好。

通常,编写软件来表达您需要的内容,并且仅表达您需要的内容。如果某个对象不需要在该函数的生命周期内持久存在,请将其保留为自动对象,并且不要使其变为静态。

请注意,无论如何通常都不需要将所有此类结构归零,因为:

  • 所使用的结构部分可能用长度或标记(例如,空字符标记结尾)指示,因此没有软件会尝试读取任何后续部分,因此无需初始化它。
  • 或者,如果将读取所有结构,则可以将该软件设计为填充非零部分,然后仅将其余部分置零,而不是先将整个结构置零。