【问题标题】:const array vs static const array in a function函数中的 const 数组与静态 const 数组
【发布时间】:2011-12-04 12:35:51
【问题描述】:

例如有一个函数在做某事。我应该如何在函数中声明和定义一个数组,我只想分配/初始化一次?

void someclass::somefunction(/*parameters here*/)
{
  static const my_array[4] = {1,2,3,4}; // #1
  /*or just*/
  const my_array[4] = {1,2,3,4}; // #2
}

据我所知,在案例#1 中,“my_array”将在数据段中分配,并在“somefunction”第一次调用时初始化一次。但是我的一个同事做了一个假设,案例#2的工作方式相同,不需要写“静态”关键字。

所以我想问一下,标准是否对案例 #1 和 #2 有所说明,如果有,具体是什么?我应该如何定义这种类型的数组以确保它只会被分配/初始化一次?

谢谢。

【问题讨论】:

  • 当我看到static const 时,我认为“这是真的不变的,对于整个程序,它只是限定在需要它的地方,这样它就不会泄漏” ,但是当我看到没有staticconst 时,我认为“这在函数的生命周期内是不变的”。他们传达了不同的意图(具体而言,static const 传达的意图与我在这种情况下从您的代码逻辑进行逆向工程的意图相同,这将建立我对您的代码可靠地传达事物的信任,从而使阅读您的代码更顺畅,减少精神压力我在未来。const 会做相反的事情。)

标签: c++ arrays static constants


【解决方案1】:

编译器将为这两个选项生成相同的代码。

您的示例非常简单,因为数组涉及普通旧数据 (POD)。标准规定选项 1 将在每次运行 somefunction 时初始化,但选项 2 将在第一次运行 somefunction 时初始化。但是,只要结果与标准中指定的结果(即所谓的 as-if 规则)无法区分,实现就可以偏离该规则。

在这种情况下,编译器将数组写入可执行文件的只读内存,并且在运行时根本没有初始化。他们可以使用 POD 类型来做到这一点。

如果您有一个需要运行时实例化的对象,那么情况会有所不同。考虑以下程序的行为:

class MyObject
{
public:
  MyObject() {}
};

void f()
{
  const MyObject arr1[1] = { MyObject() };
  static const MyObject arr2[1] = { MyObject() };
}

int main(int argc, char* argv[])
{
  f();
  f();
  return 0;
}

MyObject 的构造函数运行了 3 次。

【讨论】:

  • 我是否理解标准对分配/初始化此类数组没有任何说明?
  • 我已经扩展了我的答案,希望能解决这个问题。
  • 参考 C++ 标准(和版本)会有所帮助。
【解决方案2】:

我的编译器 (gcc 4.4.3) 在这两种情况下生成相同的代码。

源代码:

void f()
{
  static const my_array[4] = {1,2,3,4}; // #1
}

void g()
{
  const my_array[4] = {1,2,3,4}; // #2
}

生成的程序集:

f:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        leave
        ret
        .cfi_endproc

g:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        leave
        ret
        .cfi_endproc

        .section        .rodata

        .align 16
        .type   my_array.1594, @object
        .size   my_array.1594, 16
my_array.1594:
        .long   1
        .long   2
        .long   3
        .long   4

        .align 16
        .type   my_array.1591, @object
        .size   my_array.1591, 16
my_array.1591:
        .long   1
        .long   2
        .long   3
        .long   4

我不知道其他编译器的行为是否相同。

【讨论】:

    【解决方案3】:

    以最合乎逻辑和最清晰的方式声明和定义它,并且只有在分析表明它是一个瓶颈时,我才会建议更改代码。

    在这种情况下,一些编译器很可能会生成相同的代码。由于初始化语义略有不同(例如在某些情况下 g++ 使用互斥锁保护静态初始化),其他代码可能会生成不同的代码。

    事实上,确定特定编译器的唯一方法是查看反汇编。

    【讨论】:

    • 对于 POD 数据的简单初始化,在初始化期间不需要互斥锁。当然,如果有一些对象被初始化,这个问题会更有趣。
    【解决方案4】:

    在函数内部,您可能希望避免使用static,因为这也具有只初始化一次声明的效果。所以这种类型的声明(用于结构的顺序搜索)可能会导致一些细微的错误(迭代器 mapi 只会被初始化一次):

    static struct {
        int a;
        char c;
    } const someConstantMap[] = { { 1, 'a' }, { 2, 'b'}, { 0 } },
                        *mapi = someConstantMap;
    

    而省略 static 将为您提供您所期望的 const 数组和迭代器。

    【讨论】:

      猜你喜欢
      • 2011-02-02
      • 2019-09-27
      • 2011-04-18
      • 2019-05-03
      • 1970-01-01
      • 2011-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多