Часто допустимая инициализация структур данных должна обнулить всех участников. Программируя в C++, возможно, должен взаимодействовать через интерфейс с внешним API для который дело обстоит так.
Есть ли любое практическое различие между:
some_struct s;
memset(&s, 0, sizeof(s));
и просто
some_struct s = { 0 };
Люди используют обоих с методом для выбора, который более подходит для данного приложения? (Надо надеяться, подразумевается, что это только в настоящее время применимо к структурам POD; Вы получили бы все виды опустошения, если бы был станд. C++:: строка в той структуре.)
Для меня, как главным образом программист на C++, который не использует memset очень, я никогда не уверен в функциональной подписи, таким образом, я нахожу, что второй пример просто легче использовать в дополнение к тому, чтобы быть меньшим количеством ввода, более компактного, и возможно еще более очевидного, так как это говорит, что "этот объект инициализируется для обнуления" прямо в объявлении вместо того, чтобы ожидать следующей строки кода и наблюдения, "о, этот объект является инициализированным нулем".
При создании классов и структур в C++ я склонен использовать списки инициализации; мне любопытен на предмет народных мыслей о двух "C стиль" инициализации выше, а не сравнение с тем, что доступно в C++, так как я подозреваю, что многие из нас взаимодействуют через интерфейс с библиотеками C, даже если мы кодируем главным образом в C++ сами.
Править: Neil Butterworth поставил этот вопрос в продолжении, которому я верю, интересное заключение к этому вопросу.
memset
практически никогда не является правильным способом. И да, есть практическая разница (см. ниже).
В C++ не все можно инициализировать литералом 0
(объекты типа enum - нельзя), поэтому в C++ распространена идиома
some_struct s = {};
а в C -
some_struct s = { 0 };
Обратите внимание, что в C = { 0 }
- это то, что можно назвать универсальным нулевым инициализатором. Он может использоваться с объектами практически любого типа, поскольку {}
-замкнутые инициализаторы разрешены и для скалярных объектов
int x = { 0 }; /* legal in C (and in C++) */
что делает = { 0 }
полезным в общем типонезависимом коде Си (например, типонезависимые макросы).
Недостатком инициализатора = { 0 }
в C89/90 и C++ является то, что его можно использовать только как часть объявления. (В C99 эта проблема устранена введением составных литералов. Аналогичная функциональность появится и в C++.) По этой причине вы можете увидеть, как многие программисты используют memset
для того, чтобы обнулить что-то в середине C89/90 или C++ кода. Тем не менее, я бы сказал, что правильнее было бы обойтись без memset
, а использовать что-то вроде
some_struct s;
...
{
const some_struct ZERO = { 0 };
s = ZERO;
}
...
т.е. ввести "фиктивный" блок в середине кода, хотя на первый взгляд это может выглядеть не слишком красиво. Конечно, в C++ нет необходимости вводить блок.
Что касается практической разницы... Вы можете услышать от некоторых людей, что memset
на практике даст те же результаты, поскольку на практике для представления нулевых значений для всех типов используется физическая схема битов "все нули". Однако, как правило, это не так. Пример, демонстрирующий разницу в типичной реализации C++, - это тип указателя на член данных
struct S;
...
int S::*p = { 0 };
assert(p == NULL); // this assertion is guaranteed to hold
memset(&p, 0, sizeof p);
assert(p == NULL); // this assertion will normally fail
Это происходит потому, что в типичной реализации обычно используется битовая схема "все-ноль" (0xFFFF...
) для представления нулевого указателя этого типа. Приведенный выше пример демонстрирует реальную практическую разницу между обнуляющим memset
и обычным = { 0 }
инициализатором.
Другой вариант - функция bzero
.
#include <strings.h>
void bzero(void *s, size_t n);
some_struct s = { 0 };
гарантированно работает; memset
полагается на детали реализации и лучше избегать.
Надеюсь, понятно, что в настоящее время это доступно только для структур POD; вы бы получили ошибку компилятора, если бы в этой структуре была C ++ std :: string.
Нет, вы не станете . Если вы используете memset
на таком, в лучшем случае вы просто рухнете, а в худшем вы получите какую-то тарабарщину. Способ = {}
может прекрасно использоваться для структур, не относящихся к POD, если они являются агрегатами. Способ = {}
- лучший способ использовать C ++. Обратите внимание, что в C ++ нет причин помещать в него 0
, и это не рекомендуется, поскольку оно резко сокращает количество случаев, в которых его можно использовать
struct A {
std::string a;
int b;
};
int main() {
A a = { 0 };
A a = { };
}
Первый не будет делать то, что вы хотите : Он попытается создать std :: string
из C-строки с нулевым указателем на ее конструктор. Однако второй делает то, что вы хотите: он создает пустую строку.
Я думаю, что инициализация намного яснее говорит о том, что вы на самом деле делаете. Вы инициализируете структуру. Когда новый стандарт будет выпущен, этот способ инициализации будет использоваться еще шире (инициализация контейнеров с помощью {} - это то, чего стоит ожидать). Метод memset немного более подвержен ошибкам и не дает четкой информации о том, что вы делаете. Это может не иметь большого значения при программировании в одиночку, но очень много значит при работе в команде.
Для тех, кто работает с C ++, memset, malloc и т. Д. довольно эзотерические существа. Я сам сталкивался с некоторыми из них.
В зависимости от оптимизации компилятора может быть некоторый порог, выше которого memset работает быстрее, но обычно он намного превышает нормальный размер. переменных на основе стека. Использование memset для объекта C ++ с виртуальной таблицей, конечно, плохо.
Единственное практическое отличие состоит в том, что синтаксис = {0};
немного яснее говорит " инициализировать его как пустое "(по крайней мере, мне это кажется более ясным).
Чисто теоретически, есть несколько ситуаций, в которых memset
может выйти из строя, но, насколько мне известно, на самом деле это всего лишь теоретические ситуации. OTOH, учитывая, что он хуже как с теоретической , так и с практической точки зрения, мне трудно понять, почему кто-то захочет использовать memset
для этой задачи.
Я никогда не понимал загадочной доброты установки всего в ноль, которая, даже если она определена, вряд ли желательна. Поскольку это обозначено как C++, правильное решение для инициализации - дать структуре или классу конструтор.
Лучшим методом очистки структур является установка каждого поля по отдельности:
struct MyStruct
{
std::string name;
int age;
double checking_account_balance;
void clear(void)
{
name.erase();
age = 0;
checking_account_balance = 0.0;
}
};
В приведенном выше примере определен метод clear
для установки всех членов в известное состояние или значение. Методы memset
и std::fill
могут не работать из-за типов std::string
и double
.Более надежная программа очищает каждое поле индивидуально.
Я предпочитаю иметь более надежную программу, чем тратить меньше времени на набор текста.
В C я предпочитаю использовать {0,} вместо эквивалентного memset(). Однако gcc предупреждает о таком использовании :( Подробности здесь: http://www.pixelbeat.org/programming/gcc/auto_init.html
В C++ они обычно эквивалентны, но как всегда в C++ есть угловые случаи, которые нужно учитывать (отмеченные в других ответах).
Если структура содержит указатели, значение всех нулевых битов, созданное memset
, может не означать то же самое, что присвоение ему 0
в коде C (или C ++). , то есть указатель NULL
.
(Это также может быть случай с float
и doubles
, но с этим я никогда не сталкивался. Однако я не думаю, что стандарты гарантируют, что они станут равными нулю с memset
тоже.)
Edit: С более прагматической точки зрения, я бы все же сказал не использовать memset
, когда это возможно, чтобы избежать, поскольку это дополнительная функция звонить, писать дольше и (на мой взгляд) менее ясно по намерениям, чем = {0}
.