Подтвердить что ты не робот

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

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

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

Если это не так, какая разница?

4b9b3361

Ответ 1

- memset (&ampl; mystruct, 0, sizeof mystruct), такой же, как mystruct = {0};?

Нет.

memset(&mystruct, 0, sizeof mystruct) ;

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

mystruct = { 0 };

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

  • , если возможно, установите данные в mystruct равными нулю при компиляции (например, для статических переменных, tristopia и Oli Charlesworth в комментариях)
  • или , если не (например, автоматические переменные), чтобы сгенерировать код сборки, который будет устанавливать данные в ноль при инициализации переменной (что лучше, чем вызов функции для этого).

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

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

В текущем случае использование нотации mystruct = { 0 }; для инициализации a 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 } ;

Ответ 2

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

Ответ 3

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

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

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

00 xx yy zz 00 00 00 00

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

Ответ 4

memset(&mystruct, 0, sizeof mystruct);

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

mystruct = { 0 };

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

(я буду считать, что mystruct является объектом типа struct foo.)

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

struct foo mystruct = { 0 };

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

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

mystruct = (struct foo){ 0 };

где (struct foo){ 0 } - составной литерал. Смешанные литералы были введены в C99; некоторые компиляторы C, особенно Microsoft, вероятно, не поддерживают его. (Обратите внимание, что (struct foo) не является оператором трансляции, он похож на один, но не сопровождается выражением или именем типа в скобках. Это отличная синтаксическая конструкция.)

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

const struct foo foo_zero = { 0 };

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

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

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

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

struct foo mystruct = { 0 };

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

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

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

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

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

Возможно, вам удастся списать код, предполагающий, что вызов memset установит все подкомпоненты в ноль, но такой код не является строго переносимым - и Murphy Law подразумевает, что предположение не удастся в самый неудобный возможный момент, возможно, при переносе кода в важную новую систему.