обнуление памяти - PullRequest
       13

обнуление памяти

24 голосов
/ 21 мая 2010

gcc 4.4.4 C89

Мне просто интересно, что делает большинство программистов на С, когда они хотят обнулить память.

Например, у меня есть буфер 1024 байта. Иногда я делаю это:

char buffer[1024] = {0};

Который будет обнулять все байты.

Тем не менее, я должен объявить это так и использовать memset?

char buffer[1024];
.
.
memset(buffer, 0, sizeof(buffer));

Есть ли реальная причина, по которой вы должны обнулить память? Что самое худшее, что может случиться, если вы этого не сделаете?

Ответы [ 12 ]

12 голосов
/ 21 мая 2010

Я предпочитаю

char buffer[1024] = { 0 };

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

12 голосов
/ 21 мая 2010

Худшее, что может случиться? В итоге (невольно) вы получаете строку, которая не заканчивается NULL, или целое число, которое наследует то, что произошло справа от него после печати в часть буфера. Тем не менее, неопределенные строки могут происходить и другими способами, даже если вы инициализировали буфер.

Редактировать (из комментариев) Конец света также маловероятен, в зависимости от того, что вы делаете.

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

c99 содержит язык, касающийся значений инициализации по умолчанию, однако я не могу согласиться с этим gcc -std=c99, используя любой тип хранилища.

Тем не менее, так как многие старые компиляторы (и не совсем c99) все еще используются, я предпочитаю просто использовать memset()

9 голосов
/ 21 мая 2010

Когда вы определите char buffer[1024] без инициализации, вы получите в нем неопределенные данные. Например, Visual C ++ в режиме отладки будет инициализироваться с 0xcd. В режиме Release он просто выделит память и не будет заботиться о том, что будет в этом блоке с предыдущего использования.

Кроме того, ваши примеры демонстрируют инициализацию времени выполнения и времени компиляции. Если ваше char buffer[1024] = { 0 } является глобальным или статическим объявлением, оно будет храниться в сегменте данных двоичного файла с его инициализированными данными, таким образом увеличивая ваш двоичный размер примерно на 1024 байта (в данном случае). Если определение находится в функции, оно хранится в стеке и выделяется во время выполнения, а не сохраняется в двоичном файле. Если в этом случае вы предоставляете инициализатор, инициализатор сохраняется в двоичном файле, и для инициализации buffer во время выполнения делается эквивалент memcpy().

Надеюсь, это поможет вам решить, какой метод лучше всего подходит для вас.

8 голосов
/ 21 мая 2010

В данном конкретном случае нет большой разницы. Я предпочитаю = { 0 } более memset, потому что memset более подвержен ошибкам:

  • Это дает возможность получить границы неправильно.
  • Предоставляет возможность смешивать аргументы до memset (например, memset(buf, sizeof buf, 0) вместо memset(buf, 0, sizeof buf).

Как правило, = { 0 } также лучше инициализировать struct с. Он эффективно инициализирует всех членов, как если бы вы написали = 0 для инициализации каждого из них. Это означает, что члены-указатели гарантированно будут инициализированы нулевым указателем (, который может быть не все-ноль-все , а ноль-все-биты - это то, что вы получите, если использовали memset ).

С другой стороны, = { 0 } может оставить биты заполнения в struct как мусор, поэтому может быть неуместно, если вы планируете использовать memcmp для их сравнения позже.

3 голосов
/ 22 мая 2010

Одна из вещей, которая может произойти, если вы не инициализируете, - это риск утечки конфиденциальной информации.

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

3 голосов
/ 21 мая 2010

Я предпочитаю использовать memset для очистки порции памяти, особенно при работе со строками. Я хочу без сомнения знать, что после моей строки будет нулевой разделитель. Да, я знаю, что вы можете добавить \0 в конце каждой строки, и некоторые функции делают это для вас, но я не хочу сомневаться, что это произошло.

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

3 голосов
/ 21 мая 2010

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

Memset должен быть в порядке (если вы исправите размер опечатки :-)).Я предпочитаю это вашему первому примеру, потому что я думаю, что он понятнее.

Для динамически выделяемой памяти я использую calloc, а не malloc и memset.

2 голосов
/ 21 мая 2010

Это сообщение было отредактировано, чтобы сделать его правильным.Большое спасибо Tyler McHenery за то, что он указал на то, что я пропустил.

char buffer[1024] = {0};

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

Ранее я говорил:

буфер символов [1024] = {0};

Устанавливает первый символ в буфере на ноль.Этот метод обычно используется для строк с нулевым завершением, так как все данные после первого нулевого значения игнорируются последующими (не ошибочными) функциями, которые обрабатывают строки с нулевым завершением.

Что не совсем верно.Извините за недопонимание и еще раз спасибо за исправления.

2 голосов
/ 21 мая 2010

Зависит от того, как вы его заполняете: если вы планируете писать в него еще до того, как что-то прочитаете, то зачем беспокоиться? Это также зависит от того, для чего вы собираетесь использовать буфер: если он будет обрабатываться как строка, вам просто нужно установить первый байт на \0:

char buffer[1024];
buffer[0] = '\0';

Тем не менее, если вы используете его как поток байтов, то содержимое всего массива, вероятно, будет релевантным, поэтому memset можно указать всю вещь или установить { 0 }, как в вашем примере умный ход.

1 голос
/ 21 мая 2010

yup, метод calloc (), определенный в stdlib.h, выделяет память, инициализированную нулями.

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