Можно написать большую статью о разнице между двумя стилями, с которыми можно столкнуться: людьми, которые всегда инициализируют переменные при их объявлении, и людьми, которые инициализируют их при необходимости. Я делюсь большим проектом с кем-то, кто находится в первой категории, и теперь я определенно больше второго типа.
Всегда инициализация переменных принесла больше тонких ошибок и проблем, чем нет, и я попытаюсь объяснить почему, вспомнив случаи, которые я обнаружил.
Первый пример:
struct NODE Pop(STACK * Stack)
{
struct NODE node = EMPTY_STACK;
if(Stack && Stack->stackPointer)
node = Stack->node[--Stack->stackPointer];
return node;
}
Это был код, написанный другим парнем. Эта функция является самой горячей в нашем приложении (вы представляете текстовый индекс на 500 000 000 предложений в троичном дереве, стек FIFO используется для обработки рекурсии, поскольку мы не хотим использовать рекурсивные вызовы функций).
Это было типично для его стиля программирования из-за его систематической инициализации переменных. Проблема с этим кодом заключалась в скрытой memcpy
инициализации и двух других копиях структур (которые, кстати, иногда не были вызовами memcpy
gcc странно), поэтому у нас было 3 копии + вызов скрытой функции в самой горячей функция проекта.
Переписав это на
struct NODE Pop(STACK * Stack)
{
if(Stack && Stack->stackPointer)
return Stack->node[--Stack->stackPointer];
return EMPTY_STACK;
}
Только одна копия (и дополнительное преимущество для SPARC, где она выполняется, функция является конечной функцией благодаря избегаемому вызову memcpy
и не требует создания нового окна регистра). Так что функция была в 4 раза быстрее.
Еще одна проблема, которую я нашел унцию, но не помню, где именно (так что нет примера кода, извините). Переменная, которая была инициализирована при объявлении, но использовалась в цикле, с switch
в автомате конечных состояний. Проблема в том, что значение инициализации не было одним из состояний автомата, и в некоторых крайне редких случаях автомат работал некорректно. Удалив инициализатор, предупреждение, сгенерированное компилятором, сделало очевидным, что переменная может быть использована до ее правильной инициализации. Починить автомат тогда было легко.
Мораль: защитная инициализация переменной может подавить очень полезное предупреждение компилятора.
Вывод: Инициализируйте ваши переменные с умом. Делать это систематически - не что иное, как следовать культу груза (мой приятель на работе хуже, чем можно себе представить, он никогда не использует goto, всегда инициализирует переменную, использует много статических объявлений (вы знаете, это быстрее) на самом деле даже очень медленно на SPARC 64bit), делает все функции inline
, даже если они имеют 500 строк (используя __attribute__((always_inline))
, когда компилятор не хочет)