例如,有一个功能在做某事。我应该如何在函数内声明和定义一个数组,我只想分配/初始化一次?
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,如果确实如此,究竟是什么?我应该如何定义这种类型的数组,以便它只被分配/初始化一次?
谢谢。
答案 0 :(得分:5)
编译器将为这两个选项生成相同的代码。
由于数组涉及普通旧数据(POD),因此您的示例非常简单。该标准表示每次运行somefunction
时都会初始化选项1,但是第一次运行somefunction
时将初始化选项2。但是,只要结果与标准中指定的结果(即所谓的 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次。
答案 1 :(得分:4)
我的编译器(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
我不知道其他编译器的行为方式是否相同。
答案 2 :(得分:2)
以最合乎逻辑且最清晰的方式声明并定义它,并且只有在分析显示它是瓶颈的情况下才会建议更改代码。
在这种情况下,一些编译器可能会生成相同的代码。由于初始化语义略有不同,其他人可能会生成不同的代码(例如,在某些情况下,g ++通过互斥锁保护静态的初始化)。
事实上,确定您的特定编译器的唯一方法是查看反汇编。
答案 3 :(得分:0)
在函数内部,您可能希望避免使用static
,因为这也只能初始化声明一次。所以这种类型的声明(用于顺序搜索结构)可能会导致细微的错误(迭代器mapi只会被初始化一次):
static struct {
int a;
char c;
} const someConstantMap[] = { { 1, 'a' }, { 2, 'b'}, { 0 } },
*mapi = someConstantMap;
而省略static
将为您提供const数组和迭代器,如您所料。