Вопрос по c, struct – Является ли memset (& mystruct, 0, размер mystruct) таким же, как mystruct = {0} ;?

19

Я читаю об инициализированных значениях по умолчанию массива / структуры и имею этот вопрос:

являетсяmemset(&mystruct, 0, sizeof mystruct) такой же какmystruct = { 0 }; ?

если это не так, в чем разница?

@awoodland:sizeof в C это оператор, а не функция.() необязательно. Увидетьmemset(&f, 0, sizeof f); Он отлично работает для меня. Jack
@awoodland: А ты? Тогда что былоsizeof вариант, который не нуждается в паренсе? K-ballo
Почему первый комментарий был удален? комментарии сейчас кажутся немного бессмысленными. Jack
Правильно, я хотел подчеркнуть, что скобки не являются частьюsizeof но на «объекте» это работает на. То же самое сreturnчто меня очень раздражает, когда люди систематически ставят круглые скобки, как если бы это была функция. Patrick Schlüter
Люди, посмотрите, какое определениеsizeof является. Это унарный оператор, который работает либо с переменной, либо сcast expression, Как вы должны знать, приведенное выражение является типом в скобках. Следовательноsizeof сама по себе "не нужна"; скобка, это приведенное выражение, которое требует этого. Patrick Schlüter

Ваш Ответ

4   ответа
8
memset(&mystruct, 0, sizeof mystruct);

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

mystruct = { 0 };

на самом деле синтаксическая ошибка;{ 0 } не является допустимым выражением.

(Я предполагаю, чтоmystruct является объектом типаstruct foo.)

То, о чем вы, вероятно, думаете, это:

struct foo mystruct = { 0 };

где{ 0 } это инициализатор.

Если ваш компилятор поддерживает это, вы также можете написать:

mystruct = (struct foo){ 0 };

где(struct foo){ 0 } этоcompound literal, Составные литералы были введены в C99; некоторые компиляторы Си, особенно Microsoft, вероятно, не поддерживают его. (Обратите внимание, что(struct foo) являетсяnot оператор приведения; он похож на единицу, но за ним не следует выражение или имя типа в скобках. Это отдельная синтаксическая конструкция.)

Если ваш компилятор не поддерживает составные литералы, вы можете обойти его, объявив константу:

const struct foo foo_zero = { 0 };

struct foo mystruct;
/* ... */
mystruct = foo_zero;

Так вот, как они различаются по синтаксису и в том, где вы можете их использовать. Есть и семантические различия.

memset вызов устанавливает все байты, которые составляют представлениеmystruct на все нули. Это операция очень низкого уровня.

С другой стороны, инициализатор:

struct foo mystruct = { 0 };

устанавливаетfirst скалярный подкомпонентmystruct 0 и устанавливает все остальные подкомпоненты, как если бы они были инициализированы как статические объекты, т. е. равны 0. (Было бы хорошо, если бы был уборщикstruct foo mystruct = { }; синтаксис, чтобы сделать то же самое, но это не так.)

Дело в том, установить что-то0 не обязательно то же самое, что установка его представления в ноль все биты. Значение0 являетсяconverted в соответствующий тип для каждого скалярного подкомпонента.

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

Для указателей,most реализации представляют нулевые указатели как все-бит-ноль, но язык не гарантирует этого, и были реальные реализации, которые используют некоторое другое представление. (Например, использование чего-то вроде all-bits-one может упростить обнаружение разыменования нулевого указателя во время выполнения.) И представление может различаться для разных типов указателей. Смотрите раздел 5comp.lang.c FAQ.

Аналогично, для типов с плавающей запятой большинство реализаций представляют0.0 как все биты ноль, но языковой стандарт не гарантирует этого.

Вы, вероятно, можете сойти с написания кода, которыйassumes memset call установит все подкомпоненты в ноль, но такой код не является строго переносимым - иЗакон Мерфи подразумевает, что предположение не выполнится в самый неудобный момент, возможно, когда вы перенесете код на новую важную систему.

9

Теоретически есть разница. Инициализатор не требуется для инициализации заполнения, если есть некоторые вmystruct. For example:

int main(void) 
{
     struct mystruct {
          char    a;
          int     what;
     } s = {0};
}

Может содержать:

00 xx yy zz 00 00 00 00

где xx yy и zz - неопределенные байты, которые где в стеке. Компилятору разрешено это делать. Это говорит о том, что с практической точки зрения я еще не сталкивался с компилятором, который это сделал. Большинство вменяемых реализаций будут семантически обрабатывать этот случай, какmemset.

Хорошо, спасибо, я оставил свой ответ таким, какой он есть.
Это было уже в C90?
Поскольку это было не в C99, я осмелюсь сказать, что это не было в C90.
Интересно, что изменится в C11. C11 накладывает другие требования на биты заполнения? Например, еслиstruct {uint8_t b; uint32_t i;} s и один говоритs.b++; на машине, где хранилища байтов медленнее, чем хранилища слов, может ли машина выполнить приращение слова, если, когда она читаетbэто маскирует значение с0xFF (таким образом, значение битов заполнения никогда не раскрывалось, за исключением случаев, когда наложение структурыuint8[]?)
Согласно 6.7.9 (10) (в n1570), начиная с C11, заполнение должно быть установлено в нулевые биты: «если это агрегат, каждый элемент инициализируется (рекурсивно) в соответствии с этими правилами, и любое заполнение инициализируется нулевыми битами; то же самое для объединений.
11

Это полностью педантичный ответ, но, учитывая, что внутреннее представление нулевого указателя не гарантируется0 поведениеmemset в отличие от скобки-инициализация будет отличаться (memset сделал бы неправильную вещь). Тем не менее, я никогда не слышал о реализации, которая взяла на себя такую свободу, чтобы иметь не все 0-битный шаблон для нуля.

@Lundin: Откуда ты это знаешь? Там нет декларации дляmystruct подарок.
Я слышал, что на некоторых встроенных устройствах это действительно так. Никогда не встречал ни одного из них.
В конкретном случае опубликуемого OP нет указателей для инициализации.
что с плавающей точкой? Даже если memset установка двойного к нулю даст ноль (но как насчет подписанных нулей?), Я бы предпочел={ 0 } считая это логической (а не двоичной) инициализацией.
@ShinTakezou: Конечно, можно использовать инициализацию скобок, чтобы сделать инициализацию, иmemset возиться с низкоуровневыми бит-образцами памяти.
22

is memset(&mystruct, 0, sizeof mystruct) same as mystruct = { 0 }; ?

Нет.

memset(&mystruct, 0, sizeof mystruct) ;

... скажет компилятору вызвать функцию, которую мы ожидаем установитьduring execution данные в mystruct к нулю.

mystruct = { 0 };

... установит, что компилятор сам установит данные в ноль, что означает:

if possible, set the data in mystruct to zero at compilation (e.g. for static variables, as tristopia and Oli Charlesworth remarked in the comments) or if not (e.g. auto variables), to generate the assembly code that will set the data to zero when the variable is initialized (which is better than calling a function to do that).

Обратите внимание, чтоperhaps компилятор может оптимизировать memset в инструкцию времени компиляции (like replacing the first version with the second version), но я бы не стал полагаться на это какmemset это функция из библиотеки времени выполнения, а не какая-то внутренняяI'm not a compiler writer/language lawyer, though).

Исходя из C ++, моя собственная точка зрения состоит в том, что чем больше вы можете сделать во время компиляции и чем больше компилятор знает во время компиляции, даже до того, как выполняется выполнение, тем лучше: он позволяет компилятору оптимизировать код и / или генерировать предупреждение / ошибки.

В текущем случае, используяmystruct = { 0 }; нотация для инициализацииstruct всегда безопаснее, чем использовать memset, потому что это оченьvery легко написать неправильную вещь в C сmemset без жалоб компилятора.

Следующие примеры показывают, что для кода легко сделать что-то иное, чем кажется:

// only the 1st byte will be set to 0
memset(&mystruct, 0, sizeof(char)) ;          

// will probably overrun the data, possibly corrupting
// the data around it, and you hope, crashing the process.
memset(&mystruct, 0, sizeof(myLARGEstruct)) ; 

// will NOT set the data to 257. Instead it will truncate the
// integer and set each byte to 1
memset(&mystruct, 257, sizeof(mystruct)) ;    

// will set each byte to the value of sizeof(mystruct) modulo 256
memset(&mystruct, sizeof(mystruct), 0) ;      

// will work. Always.
mystruct = { 0 } ;
Выиграл четвертый пример (memset(..,..,0)) не делатьanything так как параметр размера равен нулю?
Пытатьсяtypedef struct A { int v ; } A ; void setZero(void) { A a = {0} ; } вместо. Авто переменная является локальной переменной, она не имеет ничего общего с указателями.
& quot; Во время компиляции & quot ;? Это может быть верно только для статических объектов ...
Не верно, если mystruct является автоматической переменной. В этом случае обе являются инициализацией во время выполнения, и это будет происходить при каждом вызове функции, в которой она объявлена.
@tristopia: Вы правы. Я неправильно прочитал ваш комментарий и удалил свои предыдущие комментарии, когда понял это. В любом случае, даже не говоря о небезопасности memset, я верю, что инициализация структуры с= {0} ; может привести к лучшим результатам, чем memsetting его к нулю (глядя на разборку в gcc подтверждает это, так как memset включает вызов функции).

Похожие вопросы