sizeof style: sizeof (тип) или переменная sizeof?

22

Я видел два стиля использования sizeofдля операций, связанных с памятью (например, в memsetили malloc):

  • sizeof(type), а также
  • sizeof variable или sizeof(variable)

Какой из них вы бы предпочли, или вы бы использовали сочетание двух стилей, и когда бы вы использовали каждый стиль? Каковы плюсы и минусы каждого стиля и когда вы их используете?

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

Если вы неправильно указали косвенность указателя:

type *var;
...
memset(var, 0, sizeof var);    /* oops */

Когда тип меняется:

new_type var;    /* changed from old_type to new_type */
...
memset(&var, 0, sizeof(old_type));    /* oops */
congusbongus
источник

Ответы:

30

, Я предпочитаю sizeof(variable)более sizeof(type). Рассмотреть возможность:

int a1;
float a2;
memset(&a1,0,sizeof(a1));
memset(&a2,0,sizeof(a2));

против

int a1;
float a2;
memset(&a1,0,sizeof(int));
memset(&a2,0,sizeof(float));

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

vy32
источник
4
Кроме того, если вы измените тип переменной, код, который предполагает, что тип должен будет измениться. Я думаю, что лучше выбирать правильный код с минимальной зависимостью от типа. (Я нахожусь под влиянием большого проекта преобразования 16-битных в 32-битные в те времена.)
Gort the Robot
Это предпочтительнее для работы с массивами C, где не принято создавать typedef, но использовать такие вещи, как char array [MAX_SIZE];
Mattnz
@ vy32: Неверно 1: «sizeof (array) НЕ возвращает размер массива ... размер указателя на массив» - если arrayэто действительно массив C, то sizeof(array)возвращает количество байтов, необходимое для хранения элементов массива , Если arrayуказатель на первый элемент распавшегося массива Си, то ваше утверждение выполнено. Передача массива C в качестве аргумента функции делает его похожим на указатель в функции - вся информация, доказывающая, что это был массив, как и его размер, теряется. Вот почему это распалось . Неправильно 2: «массивы на самом деле не существуют в C; ... просто синтаксический сахар для указателей».
Иоганн Герелл
9

Предпочтение (как всегда) должно отражать ваше намерение как можно более прямо.

Намерение работать против памяти существующей переменной? Если это так, то используйте sizeof(variable), так как это как можно более точно показывает, что вам важна память самой переменной.

Есть ли намерение выполнить какой-то расчет для типа, например, чтобы определить, сколько памяти должно быть выделено для нового экземпляра? Если так, то используйте sizeof(type).

То есть я предпочитаю

struct foo *bar;
bar = (struct foo *) malloc(sizeof(struct foo));

над

bar = (struct foo *) malloc(sizeof(*bar));

поскольку последний случай выглядит так, как будто вы пытаетесь получить доступ к переменной, которая еще не существует.

С другой стороны, я предпочитаю

char Buffer[256];
memset(Buffer, 0, sizeof(Buffer));

над

char Buffer[256];
memset(Buffer, 0, 256 * sizeof(char));

поскольку намерение состоит в том, чтобы явно заполнить нулем содержимое переменной, поэтому мы должны оперировать этой переменной. Попытка использовать метаданные типа просто сбивает с толку, здесь.

Эйдан Калли
источник
Я всегда так делаю, но это вопрос личного вкуса. Я считаю, что void *они автоматически приводятся к другим типам указателей, чтобы быть ошибочным.
Эйдан Калли
3

Я бы предпочел sizeof(type)более sizeof variable. Несмотря на то, что заставляет вас беспокоиться больше о типе и сделать больше изменений, это поможет вам избежать указателя ошибки, косвенного предоставления которых входят в число наиболее распространенных причин ошибок в C .

Я бы не стал так сильно беспокоиться об ошибках, когда тип sizeof(type)меняется; всякий раз, когда вы меняете тип переменной, вы должны выполнить быстрое сканирование, чтобы увидеть, где эта переменная используется и нужно ли вам изменять какие-либо sizeof(type)операторы. Несмотря на то, что всегда есть вероятность ошибиться, риск намного ниже, чем ошибки косвенного указателя.

Одним из преимуществ sizeof variableявляется использование массивов, но на практике я обнаружил, что передача массивов в виде пар first / len гораздо более распространена (в этом случае просто используется длина), плюс также очень легко получить неправильный размер из-за распад массива / указателя, как в этом примере:

ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) {
  memcpy( mat, src, sizeof( src ) );    /* NOT the same as 3*3*sizeof(float) */
}
congusbongus
источник
2
«Быстрое сканирование, чтобы увидеть, где используется эта переменная ... всегда есть шанс ошибиться» - для меня это самая большая опасность выполнения sizeof (типа), поэтому я всегда делаю sizeof (переменную). Поскольку конечным результатом этой опасности является то, что все может скомпилироваться, и даже если вы совершите одну ошибку, вам может потребоваться больше года или случайные сбои и повреждения буфера, прежде чем вы его поймаете. И я не смог установить связь (очевидно, мое временное окно на вопрос составляет около 30 секунд) между этим вопросом и «ошибками перенаправления указателя».
ДХМ
@DXM с операндом sizeof, если это значение, то оно sizeof var; если это указатель, то это sizeof *var. Это также то, что люди могут ошибаться, и компилятор с радостью примет это без жалоб. Я утверждаю, что эти ошибки гораздо сложнее обнаружить, и они встречаются чаще, чем ошибки с sizeof(type)формой.
congusbongus
1
Вы оба правы - операторы типа C и sizeof в корне нарушены и никогда не будут надежными. Ничто из того, что вы делаете как программист, не может это исправить.
Mattnz
Поскольку post помечен тегом C, публикация кода на C более информативна, чем на C ++ mat3_t::mat3_t(...).
chux - Восстановить Монику
0

Цель состоит в том, чтобы удалить избыточность. Если вы используете sizeof для операции, связанной с переменной, то, очевидно, вы должны отразить это в sizeof. Да, вы можете испортить, как в первом примере, но лекарство не использует тип, а * var, правильно соответствующий цели.

Чтобы смягчить такие проблемы, обычно используют макросы, шаблоны и аналогичные инструменты, подготовленные для данного варианта использования, а не «голый размер».

Посмотрите мой макрос CLEAR, который используется исключительно вместо голого memset. Спасла мою задницу несколько раз в случаях опечатки косвенного действия или когда содержимое структуры подобрало вектор или строку ...

Balog Pal
источник
Такие макросы привели к тому, что программирование макросов на Си дало дурную
славу
@mattnz: пожалуйста, покажи лучшую альтернативу. Проще говорить абстрактные вещи, чем создавать реально работающие программы
Balog Pal
1
Связанный Макрос - это C ++, и этот пост помечен как «C» (это разные языки), Ада была бы моим предложением.
Mattnz
@mattnz: и в той же теме показано, как применить аналогичные способы для C. Вы, похоже, полностью упускаете из виду то, что это не фактическая реализация контента, а более безопасное использование по сравнению с необработанным материалом. Кстати способы читабельны тоже.
Балог Пал
0

Бизнес логика определяет ваш выбор.

  1. Если ваш код ссылается на конкретную переменную и без этой переменной ваш код не имеет смысла - выберите sizeof(var)

  2. Если код имеет дело с набором переменных определенного типа - выберите sizeof(type). Обычно это нужно, если у вас есть typedefпеременная, которая определяет много переменных, которые вы обрабатываете по-разному в зависимости от их типа (например, сериализация). Вы можете не знать, какая из этих переменных останется в будущих версиях кода, поэтому выбор типа в качестве аргумента является логически правильным. Даже изменение такого typedefне повлияет на ваш размер строки.

Рига
источник
-1

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

Педро Перес
источник