#c #arrays #static #constants
#c #массивы #статический #константы
Вопрос:
Например, есть функция, которая что-то делает. Как я должен объявить и определить массив внутри функции, которую я хотел бы выделить / инициализировать только один раз?
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, и если да, то что именно? Как я должен определить такой тип массивов, чтобы быть уверенным, что он будет выделен / инициализирован только один раз?
Спасибо.
Комментарии:
1. Когда я вижу
static const
, я думаю: «это действительно константа, для всей программы, она просто ограничена там, где это необходимо, чтобы она не протекала», но когда я вижуconst
безstatic
, я думаю: «это константа для времени жизни функции». Они сообщают о разных намерениях (конкретно,static const
сообщают о том же намерении, которое я бы перепроектировал из вашей логики кода в этом случае, что укрепило бы мое доверие к вашему коду, надежно связывающему вещи, делая чтение вашего кода более плавным и менее напряженным для меня в будущем.const
сделал бы обратное.)
Ответ №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 раза.
Комментарии:
1. Правильно ли я понимаю, что стандарт ничего не говорит о выделении / инициализации таких типов массивов?
2. Я расширил свой ответ, чтобы, надеюсь, ответить на этот вопрос.
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 защищает инициализацию статики блокировкой мьютекса).
На самом деле единственный способ узнать наверняка для вашего конкретного компилятора — посмотреть на дизассемблирование.
Комментарии:
1. Для простой инициализации POD-данных нет необходимости в блокировках мьютекса во время инициализации. Конечно, вопрос был бы более интересным, если бы были инициализированы некоторые объекты.
Ответ №4:
Внутри функции, которую вы, возможно, захотите избежать использования static
, поскольку это также приводит к инициализации объявления только один раз. Таким образом, этот тип объявления (используемый для последовательного поиска структур) может привести к незначительным ошибкам (итератор mapi будет инициализирован только один раз):
static struct {
int a;
char c;
} const someConstantMap[] = { { 1, 'a' }, { 2, 'b'}, { 0 } },
*mapi = someConstantMap;
в то время как исключение static
даст вам массив const и итератор, как и следовало ожидать.