Должна ли инициализация локальной переменной быть обязательной? - PullRequest
11 голосов
/ 26 сентября 2008

Проблемы с обслуживанием, которые вызывают неинициализированные локальные пользователи (особенно указатели), будут очевидны для всех, кто немного занимался обслуживанием или улучшением c / c ++, но я все еще вижу их и иногда слышу последствия производительности, приведенные в качестве их оправдания.

В c легко показать, что избыточная инициализация оптимизирована:

$ less test.c
#include <stdio.h>
main()
{
#ifdef INIT_LOC
    int a = 33;
    int b;
    memset(&b,66,sizeof(b));
#else
    int a;
    int b;
#endif
    a = 0;
    b = 0;
    printf ("a = %i, b = %i\n", a, b);
}

$ gcc --version
gcc (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)

[Не оптимизировано:]

$ gcc test.c -S -o no_init.s; gcc test.c -S -D INIT_LOC=1 -o init.s; diff no_in
it.s init.s
22a23,28
>       movl    $33, -4(%ebp)
>       movl    $4, 8(%esp)
>       movl    $66, 4(%esp)
>       leal    -8(%ebp), %eax
>       movl    %eax, (%esp)
>       call    _memset
33a40
>       .def    _memset;        .scl    3;      .type   32;     .endef

[Оптимизированная:]

$ gcc test.c -O -S -o no_init.s; gcc test.c -O -S -D INIT_LOC=1 -o init.s; diff
 no_init.s init.s
$

Таким образом, производительность WRT при каких обстоятельствах обязательная инициализация переменной НЕ является хорошей идеей?

ЕСЛИ применимо, нет необходимости ограничивать ответы на c / c ++, но, пожалуйста, будьте внимательны с языком / средой (и воспроизводимые доказательства гораздо предпочтительнее предположений!)

Ответы [ 17 ]

1 голос
/ 12 ноября 2008

Да: всегда инициализируйте ваши переменные, если у вас нет очень веской причины не делать этого. Если мой код не требует определенного начального значения, я часто инициализирую переменную значением, которое будет гарантировать явную ошибку, если следующий код не работает.

1 голос
/ 26 сентября 2008

Всегда инициализируйте локальные переменные по крайней мере до нуля. Как вы видели, это не реальная производительность.

int i = 0;
struct myStruct m = {0};

Вы в основном добавляете 1 или 2 инструкции по сборке, если это так. Фактически, многие среды выполнения C будут делать это для вас при сборке "Release", и вы ничего не измените.

Но вы должны его инициализировать, потому что теперь у вас будет эта гарантия.

Одна из причин не инициализации связана с отладкой. Некоторые среды выполнения, например MS CRT инициализирует память заранее определенными и задокументированными шаблонами, которые вы можете идентифицировать. Поэтому, когда вы просматриваете память, вы видите, что память действительно неинициализирована, и она не использовалась и не сбрасывалась. Это может быть полезно при отладке. Но это во время отладки.

0 голосов
/ 26 сентября 2008

Как вы показали в отношении перформанса, это не имеет значения. Компилятор будет (в оптимизированных сборках) определять, записана ли локальная переменная без чтения и удаляет код, если у него нет других побочных эффектов.

Тем не менее: если вы инициализируете что-то с помощью простых выражений, просто чтобы убедиться, что оно инициализировано, это нормально. Я лично не делаю этого по одной причине:

Это обманывает парней, которые впоследствии могут поддерживать ваш код, в мысли, что инициализация необходима. Это маленькое foo = 0; увеличит сложность кода. Кроме этого, это просто вопрос вкуса.

Если вы нестандартно инициализируете переменные с помощью сложных операторов, это может иметь побочный эффект.

Например:

  float x = sqrt(0);

Может быть оптимизирован вашим компилятором, если вам повезет и вы будете работать с умным компилятором. С не очень умным компилятором это может также привести к дорогостоящему и ненужному вызову функции, потому что sqrt может - как побочный эффект - установить переменную errno.

Если вы называете функции, которые вы определили сами, моя лучшая ставка в том, что компилятор всегда предполагает, что они могут иметь побочные эффекты, а не оптимизировать их. Это может отличаться, если функция находится в одном и том же модуле перевода или у вас включена оптимизация всей программы.

0 голосов
/ 30 сентября 2008

Просто вторичное наблюдение. Инициализации ЛЕГКО оптимизируются только для примитивных типов или при назначении константными функциями.

a = foo ();

a = foo2 ();

Не может быть легко оптимизировано, потому что foo может иметь побочные эффекты.

Кроме того, выделение кучи раньше времени может привести к огромным потерям производительности. Возьми код вроде

void foo(int x)

{

ClassA * instance = new ClassA ();

// ... сделать что-то не связанное с "экземпляром" ... если (х> 5) {

delete instance;

return;

}

// .. делать то, что использует экземпляр

}

В этом случае просто объявите экземпляр именно тогда, когда вы будете его использовать, и инициализируйте его только там. И нет Компилятор не может оптимизировать это для вас, так как у конструктора могут быть побочные эффекты, которые изменят переупорядочение кода.

edit: я не могу использовать функцию листинга кода: P

0 голосов
/ 26 сентября 2008

Я думаю, что в большинстве случаев плохая идея инициализировать переменные значением по умолчанию, потому что оно просто скрывает ошибки, которые легко найти с помощью неинициализированных переменных. Если вы забыли получить и установить фактическое значение или случайно удалили полученный код, вы, вероятно, никогда не заметите его, потому что 0 во многих случаях является разумным значением. В большинстве случаев эти ошибки гораздо проще вызвать со значением >> 0.

Например:


void func(int n)
{
    int i = 0;

    ... // Many lines of code

    for (;i < n; i++)
        do_something(i);

Через некоторое время вы собираетесь добавить еще кое-что.


void func(int n)
{
    int i = 0;

    for (i = 0; i < 3; i++)
        do_something_else(i);

    ... // Many lines of code

    for (;i < n; i++)
        do_something(i);

Теперь ваш второй цикл начинается не с 0, а с 3, в зависимости от того, что делает функция, может быть очень трудно обнаружить, что есть даже ошибка.

0 голосов
/ 26 сентября 2008

В качестве простого примера, вы можете определить, к чему это будет инициализироваться (C / C ++)?

bool myVar;

У нас была проблема с продуктом, который иногда рисовал изображение на экране, а иногда нет, обычно в зависимости от того, с какой машиной он был построен. Оказалось, что на моей машине оно инициализировалось как false, а на коллеге - как true.

0 голосов
/ 26 сентября 2008

Иногда переменная используется для «сбора» результата более длинного блока вложенных ifs / elses ... В этих случаях я иногда оставляю переменную неинициализированной, потому что должна быть инициализирована позже одним условных веток.

Хитрость в том, что если я сначала оставляю его неинициализированным, а затем в длинном блоке if / else возникает ошибка, поэтому переменная никогда не назначается, я вижу эту ошибку в Valgrind :-), которая, конечно же, требует частого запуска код (в идеале регулярные тесты) через Valgrind.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...