memset на массиве структур в C ++ - PullRequest
0 голосов
/ 07 мая 2010

У меня есть другой вопрос memset. Похоже, что у кода, который я редактирую, могут быть некоторые проблемы (или это не делается одинаково в разных файлах)

A::LRM las[9]; //A and LRM are both structures with BOOLS and INTS
memset(&las, 0, sizeof(las));

typedef Sec SecArray[16];
SecArray rad_array;
memset(rad_array, 0, sizeof(SecArray));

Второй пример выглядит правильно, поскольку rad_array совпадает с первой позицией в массиве. Тогда sizeof(SecArray)) будет иметь смысл. Первый не кажется мне правильным. Все структуры являются просто BOOL и INTS ничего динамического в них.

Из моего другого поста о memset я понял, что он следует этому формату.

memset("pointer to object", "what to set it to", "size of object")

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

Ответы [ 5 ]

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

Оба вызова на memset верны. И sizeof(las) (или просто sizeof las) и sizeof(SecArray) вернут размер всего массива.

Если вас беспокоит аргумент first , то снова оба сработают. Указатель на весь массив (&las) или указатель на первый элемент (rad_array в этом контексте) будет одинаково хорошо работать с memset.

В целом, что касается использования sizeof, я бы рекомендовал использовать первый подход, поскольку он не зависит от типа. Хорошая практика программирования - избегать упоминания имен типов в ваших операторах, то есть максимально ограничивайте имена типов объявлениями.

Что касается первого аргумента, в случае массива это вопрос предпочтения, но в целом я бы сказал, что если у вас есть объект типа T

T t;

и вы хотите заполнить его нулями, используя memset, вы обычно делаете это как

memset(&t, 0, sizeof t);

Я не понимаю, почему массив должен быть исключением из этого правила (особенно, если точная природа типа скрыта за typedef-именем). Вышеуказанный memset(&t, ... будет работать независимо от того, является ли T типом массива, типом структуры или любым другим типом. Я не понимаю, почему нужно внезапно отбрасывать & только потому, что T является типом массива. Скорее наоборот, я бы оставил это &, чтобы сохранить код как можно более независимым от типа.

Наконец, в C ++ в обоих случаях лучший способ сделать это - вообще не использовать memset и просто

A::LRM las[9] = {};
SecArray rad_array = {};

вместо.

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

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

memset("pointer to memory", "what to set each byte to", "number of bytes to set")

Так что на самом деле это лучше:

A::LRM las[9]; //A and LRM are both structures with BOOLS and INTS
memset(&las, 0, sizeof(A::LRM)*9);

typedef Sec SecArray[16];
SecArray rad_array;
memset(rad_array, 0, sizeof(Sec)*16);

Оба приведенных вами примера на самом деле будут работать, но они хрупкие, поскольку их легкомассив, чтобы распасться на указатель, после чего он не будет работать должным образом (например, если вы передали массив в функцию, которая выполняет memset, или переключились на динамическое выделение с помощью new).

0 голосов
/ 07 мая 2010

memset заполняет память нулевыми байтами. В общем, это не то же самое, что инициализация членов в ноль. Пока мы придерживаемся стандарта ISO C ++ (или C, в этом отношении), единственная гарантия, которую вы имеете, состоит в том, что all-bits-0 является действительным значением char или unsigned char, равным 0. Даже для целочисленных типов это не обязательно должно быть истиной, поскольку им разрешено иметь заполнение вне битов значения, и все ноль для этих битов может быть представлением ловушки.

Это определенно неправильный способ инициализации float или double до 0.0 или указателя на null. Существуют совместимые реализации, для которых ноль все биты не является значением 0 для этих типов.

В любом случае, ваш код может быть написан как намного короче, так и полностью переносимым способом, используя пустой инициализатор агрегата:

A::LRM las[9] = {};

Это будет инициализировать значение массива - который, в свою очередь, инициализирует нулями все элементы - который, для структур POD, инициализирует нулями все поля.

Конечно, это предполагает, что A::LRM является структурой POD. Если это не так, тогда memset еще более вероятно сломает вещи, и вам действительно нужно просто использовать его конструктор и присвоить любые поля, которые конструктор не инициализировал.

0 голосов
/ 07 мая 2010

Ваш первый пример будет работать, но я рекомендую:

A::LRM las[9]; 
memset(las, 0, sizeof(las)); // <== no ampersand needed

Обратите внимание, что не имеет значения, что такое A, A::LRM - это тип данных.

НО

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

0 голосов
/ 07 мая 2010

Последний аргумент memset - это не размер объекта, а размер «массива» объектов, на которые указывает «указатель на объект».

Допустим, у вас есть:

memset(ptr, value, length);

memset установит все байты между ptr и ptr+sizeof(*ptr)*length-1 в value.

...