Это хорошая практика для инициализации массива в C / C ++? - PullRequest
9 голосов
/ 11 января 2010

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

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

Это заставило меня задать вопрос, является ли хорошей практикой инициализация массива также в C / C ++, как это делается в Java?

Ответы [ 8 ]

23 голосов
/ 11 января 2010

Хорошей практикой является инициализация памяти / переменных перед их использованием - неинициализированные переменные являются большим источником ошибок, которые зачастую очень трудно отследить.

Инициализация всех данных - очень хорошая идея при записи их в формат файла: он обеспечивает чистоту содержимого файла, поэтому с ним легче работать, и он менее подвержен проблемам, если кто-то неправильно пытается «использовать» неинициализированные данные помните, что это может быть не только ваш собственный код, который читает данные в будущем), но и делает файлы намного более сжимаемыми.

Единственная веская причина не инициализировать переменные перед их использованием - это критические для производительности ситуации, когда инициализация технически "ненужна" и требует значительных накладных расходов. Но в большинстве случаев инициализация переменных не принесет существенного вреда (особенно если они объявлены только непосредственно перед их использованием), но сэкономит вам много времени на разработку за счет устранения общего источника ошибок.

5 голосов
/ 11 января 2010

Использование неопределенного значения в массиве приводит к неопределенному поведению. Таким образом, программа может давать разные результаты. Это может означать, что ваши файлы будут немного отличаться, или что программа вылетает, или программа форматирует ваш жесткий диск, или программа заставляет демонов вылететь из носа пользователя (http://catb.org/jargon/html/N/nasal-demons.html)

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

MyStruct array[10];
printf( "%f", array[2].v ); // POTENTIAL BANG!
array[3].v = 7.0;
...
printf( "%f", array[3].v ); // THIS IS OK.

Не забывайте, что для огромных массивов POD есть хороший способ инициализации всех членов до нуля

MyPODStruct bigArray[1000] = { 0 };
3 голосов
/ 11 января 2010

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

По вашему конкретному вопросу рекомендуется также перезаписывать неиспользуемые части перед их записью в файл, поскольку они могут содержать что-то из предыдущего использования, которое вы не хотите записывать, например пароли.

1 голос
/ 16 апреля 2010

Можно написать большую статью о разнице между двумя стилями, с которыми можно столкнуться: людьми, которые всегда инициализируют переменные при их объявлении, и людьми, которые инициализируют их при необходимости. Я делюсь большим проектом с кем-то, кто находится в первой категории, и теперь я определенно больше второго типа. Всегда инициализация переменных принесла больше тонких ошибок и проблем, чем нет, и я попытаюсь объяснить почему, вспомнив случаи, которые я обнаружил. Первый пример:

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)), когда компилятор не хочет)

1 голос
/ 11 января 2010

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

Плохо читать из неинициализированных массивов. Хорошо иметь их без чтения из неинициализированных мест.

Более того, если в вашей программе есть ошибка, из-за которой она читается из неинициализированного места в массиве, то «закрытие» путем защитной инициализации всего массива известным значением не является решением для ошибки, и может только вывести ее на поверхность позже. *

1 голос
/ 11 января 2010

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

Но если вы используете массив char как строку с нулевым символом в конце, то вы сможете записать его в файл с соответствующей функцией.

Хотя в c ++ может быть лучше использовать более ООП-решение. И.Е. векторы, строки и т. д.

0 голосов
/ 11 января 2010

Я бы сказал, что хорошей практикой в ​​C ++ является использование std :: vector <> вместо массива. Конечно, это не относится к Си.

0 голосов
/ 11 января 2010

Во-первых, вы должны инициализировать массивы, переменные и т. Д., Если этого не произойдет, это повлияет на правильность вашей программы.

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

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

...