Как memset массив символов с завершающим нулевым символом?

Каков правильный и безопасный способ memset всего массива символов с нулевым завершающим символом? Я могу перечислить несколько применений:

...
char* buffer = new char [ARRAY_LENGTH];

//Option 1:             memset( buffer, '\0', sizeof(buffer) );
//Option 2 before edit: memset( buffer, '\0', sizeof(char*) * ARRAY_LENGTH );
//Option 2 after edit:  memset( buffer, '\0', sizeof(char) * ARRAY_LENGTH );
//Option 3:             memset( buffer, '\0', ARRAY_LENGTH );
...
  • Имеет ли какой-либо из них значительное преимущество перед другими?
  • С какими проблемами я могу столкнуться при использовании 1, 2 или 3?
  • Как лучше всего обработать этот запрос?

person Koray    schedule 17.10.2012    source источник
comment
Вариант 2 неверен, должно быть sizeof(char), что всегда 1.   -  person hmjd    schedule 17.10.2012
comment
У вас есть тег c, но новый char[ARRAY_LENGTH] не является допустимым синтаксисом C.   -  person Barmar    schedule 17.10.2012
comment
Ни один из ваших вариантов даже не правильный! Правильным способом вызова memset было бы memset(buffer, '\0', sizeof(char) * ARRAY_LENGTH);. Однако синтаксис, указанный Лучианом, лучше.   -  person Lundin    schedule 17.10.2012
comment
Извините за ошибки тегов и опечатки - только что отредактировал вопрос   -  person Koray    schedule 17.10.2012
comment
@Lundin sizeof(char) по определению равен 1. Когда я вижу это, я фактически использую его как знак, указывающий на дополнительную тщательную проверку кода.   -  person Art    schedule 17.10.2012
comment
Не используйте new[] в C++, используйте соответствующий контейнер.   -  person GManNickG    schedule 17.10.2012


Ответы (8)


Варианты один и два просто неверны. Первый использует размер указателя вместо размера массива, поэтому он, вероятно, не будет записывать весь массив. Второй использует sizeof(char*) вместо sizeof(char), поэтому он будет писать за конец массива. Вариант 3 подходит. Вы также можете использовать это

memset( buffer, '\0', sizeof(char)*ARRAY_LENGTH );

но sizeof(char) гарантированно равно 1.

person Dirk Holsopple    schedule 17.10.2012
comment
Второй вариант использует sizeof(char), а не sizeof(char*), или я что-то упустил? - person PiotrNycz; 17.10.2012
comment
@PiotrNycz: судя по комментарию hmjd под вопросом, в вопросе изначально говорилось sizeof(char*) в варианте 2, но он был изменен в окне редактирования, поэтому истории редактирования нет. - person Steve Jessop; 17.10.2012
comment
@LuchianGrigore: из-за этого ответы других людей выглядят неправильно - три человека сказали, что вариант 2 подходит. - person Steve Jessop; 17.10.2012
comment
Не так важно, но передача 0 вместо '\0' тоже работает. - person Bharath; 01.10.2015
comment
Предположим, что массив равен char a[200][200];. memset(a, 0, sizeof(a)); нормально работает? - person ajaysinghnegi; 22.06.2019
comment
не работает на ардуино - person dbow; 03.01.2021

Идиоматический способ - это инициализация массива значением:

char* buffer = new char [ARRAY_LENGTH]();

Вариант 1 устанавливает только первые sizeof(char*) байта в 0 или приводит к неопределенному поведению, если ARRAY_LENGTH < sizeof(char*). Это связано с использованием размера указателя вместо размера типа.

Вариант 2 приводит к неопределенному поведению, поскольку вы пытаетесь установить более ARRAY_LENGTH байт. sizeof(char*) почти наверняка больше 1.

Поскольку это C++ (нет new в C), я предлагаю вместо этого использовать std::string.

Для C (при условии, что malloc вместо new[]) вы можете использовать

memset( buffer, 0, ARRAY_LENGTH );
person Luchian Grigore    schedule 17.10.2012

Поскольку вопрос постоянно меняется, я определяю:

1: memset( buffer, '\0', sizeof(buffer) );

2a: memset( buffer, '\0', sizeof(char*) * ARRAY_LENGTH );

2b: memset( buffer, '\0', sizeof(char) * ARRAY_LENGTH );

3: memset( buffer, '\0', ARRAY_LENGTH );

Если вопрос состоит просто в том, «как правильно вызвать memset», а не в том, «как лучше всего обнулить этот массив», то правильным будет либо 2b, либо 3. 1 и 2а неверны.

У вас может быть война стилей из-за 2b против 3: включать ли sizeof(char) или нет - некоторые люди опускают его, потому что он избыточен (я обычно так делаю), другие люди вставляют его, чтобы создать своего рода согласованность с той же настройкой кода. массив int. То есть они всегда умножают размер на количество элементов, даже если знают, что размер равен 1. Один из возможных выводов состоит в том, что «самый безопасный» способ memset для массива, на который указывает buffer, это:

std::memset(buffer, 0, sizeof(*buffer) * ARRAY_LENGTH);

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

Другой вариант, любимый программистами "C++ не C", это:

/* never mind how buffer is allocated */
std::fill(buffer, buffer + ARRAY_LENGTH, 0);

Если вам не все равно, вы можете сами проверить, оптимизирует ли ваш компилятор это для того же кода, для которого он оптимизирует эквивалентный вызов std::memset.

char *buffer = new char [ARRAY_LENGTH](); изящно, но на практике почти бесполезно в C++, потому что вы почти никогда не выделяете массив с new в первую очередь.

std::string buffer(ARRAY_LENGTH, 0); представляет особый способ управления буфером, который может быть или не быть тем, что вам нужно, но часто так и есть. В некоторых случаях можно многое сказать о char buffer[ARRAY_LENGTH] = {0};.

person Steve Jessop    schedule 17.10.2012

  • Имеет ли какой-либо из них значительное преимущество перед другими?
  • С какими проблемами я могу столкнуться при использовании 1, 2 или 3?

1-е неверно, потому что sizeof(buffer) == sizeof(char*).

2-й и 3-й в порядке.

  • Как лучше всего обработать этот запрос?

Почему бы просто не:

buffer[0] = '\0';

Если это массив char, зачем возиться с остальными символами? С первым байтом, установленным в ноль, у вас есть эквивалент "" в вашем buffer.

Конечно, если вы действительно настаиваете на обнулении всех buffer, используйте ответ с std::fill - это правильный путь. Я имею в виду std::fill(buffer, buffer + ARRAY_LENGTH, 0);.

person PiotrNycz    schedule 17.10.2012

Если вам абсолютно необходимо использовать необработанный массив в C++ (это очень плохая идея), сделайте это так:

char* buffer = new char [ARRAY_LENGTH]();

Для C++ memset обычно является последним прибежищем некомпетентных, хотя за последние несколько месяцев я узнал, что для приемлемой производительности с текущими инструментами необходимо опускаться до этого уровня, когда реализуется собственный строковый класс.

Вместо этих необработанных массивов и т. д., которые могут потребовать memset, используйте, например. std::string (для приведенного выше случая), std::vector, std::array и т. д.

person Cheers and hth. - Alf    schedule 17.10.2012
comment
это очень плохая идея. Ага? Тогда как бы вы написали код для обработки массива значений 8-битных аппаратных регистров? - person Lundin; 17.10.2012
comment
@Lundin - вам, вероятно, не следует спрашивать этих парней - они придумают какую-нибудь неприятную настройку метаданных std::vector, чтобы они указывали на аппаратное обеспечение с отображением памяти ‹g› - person Martin James; 17.10.2012
comment
Начиная с C++ 11, я бы использовал: std::unique_ptr<char[]> buffer(new char[ARRAY_LENGTH]());, а начиная с C++ 14, я бы использовал: std::unique_pointer<char[]> buffer = std::make_unique<char[]>(ARRAY_LENGTH); Или альтернативу общего указателя, если это необходимо. - person Amit G.; 29.07.2018
comment
@AmitG.: Хм. Я бы предпочел auto buffer = make_unique<char[]>(array_length);, если бы мне пришлось иметь необработанный массив, выделенный new. - person Cheers and hth. - Alf; 29.07.2018

Начиная с С++ 11, я бы выбрал:

#include <array>

std::array<char, ARRAY_LENGTH> buffer{ '\0' };

buffer.fill('\0');
person Amit G.    schedule 29.07.2018
comment
Можете ли вы уточнить, почему? Я нахожу это еще менее читаемым, чем memset(), и вы не сможете скомпилировать его старыми компиляторами. Дает ли это какое-то преимущество во время выполнения? - person Niko; 19.02.2019

Option 3: memset( buffer, '\0', ARRAY_LENGTH ): даст вам только длину массива, но на самом деле этот параметр определяет количество байтов памяти.

Option 1: memset( buffer, '\0', sizeof(buffer) ): даст вам неверный ответ, потому что buffer это char*. sizeof(buffer) не даст вам размер всего массива, а только размер переменной-указателя.

Вариант 2 правильный.

person taufique    schedule 17.10.2012

Ну лично мне нравится вариант 3:

memset( buffer, '\0', ARRAY_LENGTH )

ARRAY_LENGTH это именно то, что я хотел бы заполнить в памяти.

person Simon Lau    schedule 28.07.2018