Подтвердить что ты не робот

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

gcc 4.4.4 C89

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

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

char buffer[1024] = {0};

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

char buffer[1024] = { 0 };

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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

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

Ответ 6

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

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

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

Ответ 7

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

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

Ответ 8

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

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

Ответ 9

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

char buffer[1024] = {0};

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

Ранее я сказал:

char buffer [1024] = {0};

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

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

Ответ 10

Я также использую memset (buffer, 0, sizeof (buffer));

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

Всегда memset-to to 0 после malloc - очень хорошая практика.

Ответ 11

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

Ответ 12

Я не знаком с:

char buffer[1024] = {0};

техника. Но, полагая, что он делает то, что я думаю, он имеет (потенциальную) разницу в двух методах.

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

Последний будет выполнен в RUN TIME.

Первое может повлечь за собой некоторое время загрузки. Если у вас есть только:

char buffer[1024];

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

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

Разум, ни у одного из них нет "реальных" воздействий на производительность в малом. У них может не быть "большого". Просто говоря, здесь есть потенциал, и эти два метода на самом деле делают что-то совсем другое.