В Win32 программирование API это типично для использования C struct
s с несколькими полями. Обычно только несколько их имеют значимые значения, и все другие должны быть обнулены. Это может быть достигнуто любым из этих двух способов:
STRUCT theStruct;
memset( &theStruct, 0, sizeof( STRUCT ) );
или
STRUCT theStruct = {};
Второй вариант смотрит инструмент для очистки - это - острота, это не имеет никаких параметров, которые могли быть введены с опечаткой и вывод к ошибке, будучи установленным.
Это имеет какие-либо недостатки по сравнению с первым вариантом? Какой вариант использовать и почему?
Эти две конструкции очень различаются по своему значению. Первый использует функцию memset
, которая предназначена для установки буфера памяти на определенное значение . Второй для инициализирует объект . Позвольте мне пояснить это с помощью небольшого кода:
Предположим, у вас есть структура, которая имеет члены только типов POD
struct POD_OnlyStruct
{
int a;
char b;
};
POD_OnlyStruct t = {}; // OK
POD_OnlyStruct t;
memset(&t, 0, sizeof t); // OK as well
В этом случае мы пишем POD_OnlyStruct t = {}
или POD_OnlyStruct t; memset (& t, 0, sizeof t)
не имеет большого значения, поскольку единственное различие, которое у нас есть, это то, что байты выравнивания устанавливаются в нулевое значение в случае memset
. Поскольку у вас обычно нет доступа к этим байтам, для вас нет никакой разницы.
С другой стороны, поскольку вы пометили свой вопрос как C ++, давайте попробуем другой пример, с типами членов , отличными от POD :
struct TestStruct
{
int a;
std::string b;
};
TestStruct t = {}; // OK
{
TestStruct t1;
memset(&t1, 0, sizeof t1); // ruins member 'b' of our struct
} // Application crashes here
В этом случае с использованием такого выражения, как TestStruct t = {}
- это хорошо, а использование memset
в нем приведет к сбою. Вот что произойдет, если вы используете memset
- создается объект типа TestStruct
, таким образом создается объект типа std :: string
, поскольку он является членом наша структура. Затем memset
устанавливает в памяти, где находился объект b
, определенное значение, например ноль. Теперь, когда наш объект TestStruct выходит за пределы области видимости, он будет уничтожен, и когда дойдет очередь до его member std :: string b
, вы увидите сбой, так как все внутренние структуры этого объекта были разрушены memset
.
Итак, на самом деле эти вещи очень разные , и хотя иногда вам нужно memset
целую структуру в определенных случаях, всегда важно убедиться, что вы понять, что вы делаете, и не ошибиться, как в нашем втором примере.
Мой голос - используйте memset
для объектов , только , если это необходимо, и используйте инициализацию по умолчанию x = {}
в все остальные случаи.
Я бы использовал инициализацию значения, потому что она выглядит чистой и менее склонной к ошибкам, как вы уже упоминали. Я не вижу в этом недостатка.
Вы можете полагаться на memset
для обнуления структуры после ее использования.
не то, чтобы это было обычным делом, но я думаю, что второй способ также имеет преимущество инициализации плавающих чисел до нуля. Делая мемсет, конечно же, не
. Инициализировать значение, так как это можно сделать во время компиляции
.
Также корректно 0 инициализирует все типы POD.
Memset() выполняется во время исполнения.
.
Также подозрительно использование memset(), если структура не POD.
.
Неправильно инициализирует (до нуля) не типы int.
Если указателей много и вы, скорее всего, добавите больше в будущем, это может помочь в использовании memset. В сочетании с соответствующими вызовами assert(structuret->member)
вы можете избежать случайных сбоев при попытке распознать плохой указатель, который вы забыли инициализировать. Но если Вы не такой забывчивый, как я, то член-инициализация, наверное, лучшая!
Однако, если Ваша структура используется как часть публичного API, то Вы должны получить клиентский код, чтобы использовать memset в качестве требования. Это поможет в дальнейшей проверке, так как Вы можете добавлять новые члены, а клиентский код автоматически NULLL их в вызове memset, вместо того, чтобы оставлять их в (возможно, опасном) неинициализированном состоянии. Это то, что вы делаете, например, при работе со структурами сокетов
.] В зависимости от членов структуры эти два варианта не обязательно эквивалентны. []memset[
] установит структуру в состояние all-bits-zero, в то время как инициализация значения инициализирует всех членов в состояние zero. Стандарт языка Си гарантирует, что они будут одинаковыми только для интегральных типов, а не для значений с плавающей точкой или указателей.[
] Также, некоторые API требуют, чтобы структура действительно была установлена на all-bits-zero. Например, API сокета Berkeley использует структуры полиморфно, и там важно действительно установить всю структуру на ноль, а не только видимые значения. В документации API должно быть сказано, действительно ли структура должна быть all-bits-zero, но это может быть недостаточно.[
]. [] Но если ни один из них, или подобный случай, не применим, то все зависит от вас. При определении структуры я бы предпочёл инициализацию значения, так как это более чётко передаёт намерение. Конечно, если вам нужно обнулить существующую структуру, то []memset[
] - это единственный выбор (ну, кроме инициализации каждого члена в ноль вручную, но это обычно не делается, особенно для больших структур).[
Если Ваша структура содержит такие вещи, как :
int a;
char b;
int c;
, то байты отступов будут вставляться между "b" и "c". memset() обнуляет их, в противном случае - нет, так что будет 3 байта мусора (если Ваши байты 32 бита). Если вы собираетесь использовать свою структуру для чтения/записи из файла, это может быть важно.
В некоторых компиляторах STRUCT theStruct = {};
преобразуется в memset (& theStruct, 0, sizeof (STRUCT));
в исполняемом файле. Некоторые функции C уже связаны для настройки среды выполнения, поэтому у компилятора есть такие библиотечные функции, как memset / memcpy, доступные для использования.