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 */

Відповіді:


30

, Я вважаю за краще sizeof(variable)більш sizeof(type). Поміркуйте:

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

vs.

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

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


4
Крім того, якщо ви зміните тип змінної, код, який передбачає тип, повинен буде змінитися. Я думаю, що краще використовувати правильний код з якомога меншою залежністю від типу. (На мене впливає великий проект перетворення 16-ти до 32-ти бітових версій.)
Gort the Robot

Це краще для роботи з масивами C, де не часто створювати typedef, але використовувати такі речі, як масив char [MAX_SIZE];
mattnz

@ vy32: Неправильно 1: "sizeof (масив) НЕ повертає розмір масиву ... розмір вказівника на масив" - якщо arrayце дійсно C масив, то sizeof(array)повертає кількість байтів, необхідних для зберігання елементів масиву . Якщо arrayвказівник на перший елемент розкладеного масиву C, то ваше твердження має місце. Якщо передавати масив 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) */
}

2
"Швидке сканування, щоб побачити, де використовується ця змінна .... завжди є шанс отримати цю помилку" - для мене це найбільша небезпека робити sizeof (type), тому я завжди роблю sizeof (змінну). Оскільки кінцевим результатом цієї небезпеки є те, що все може все-таки зібратись, і навіть якщо ви зробите одну помилку, це може зайняти у вас більше року або випадкові збої та пошкодження буфера, перш ніж ви їх зловите. І я не зміг встановити зв’язок (очевидно, що моє вікно часу на кожне запитання становить приблизно 30 сек) між цим питанням та "помилками вказівки".
DXM

@DXM З операндом sizeof, якщо це значення, то це sizeof var; якщо це покажчик це sizeof *var. Це також те, що люди можуть помилитися, і компілятор із задоволенням сприйме це без нарікань. Я стверджую, що ці помилки помітити набагато важче і є більш поширеними, ніж помилки з sizeof(type)формою.
congusbongus

1
Ваші права - тип C і розмір оператора принципово порушені і ніколи не будуть надійними, нічого, що ви робите як програміст, не зможе це виправити.
mattnz

Оскільки повідомлення позначено тегом C, розміщення коду С є більш інформативним, ніж код C ++ mat3_t::mat3_t(...).
chux

0

Мета - усунути надмірність. Якщо ви використовуєте sizeof для операції, пов'язаної зі змінною, то, очевидно, ви повинні відобразити це у sizeof. Так, ви можете зіпсувати, як у першому прикладі, але для лікування не використовується тип, а * var, правильно відповідає меті.

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

Дивіться мій CLEAR макрос, який використовується виключно замість голого мемсета. Збережено мою дупу кілька разів у випадках невдалої друку або коли вміст структури підхопив вектор або рядок ...


Такі макроси дали те, що програмування макросів C погано назвали ......
mattnz

@mattnz: покажіть, будь ласка, кращу альтернативу. Простіше говорити абстрактні речі, ніж створювати фактично працюючі програми
Balog Pal

1
Зв'язаний макрос - це C ++, і ця публікація позначена "C" (це різні мови), моя пропозиція - Ада.
mattnz

@mattnz: і в тій же темі показано, як домогтися виконання подібних способів для C. Ви, здається, повністю пропускаєте суть, що це не реальна реалізація вмісту, а безпечніше використання порівняно із сировиною. Btw способи читати теж.
Balog Pal

0

Логіка бізнесу визначає ваш вибір.

  1. Якщо ваш код посилається на певну змінну і без цієї змінної, ваш код не має сенсу - вибирайте sizeof(var)

  2. Якщо ви кодуєте набір змінних певного типу - виберіть sizeof(type). Зазвичай вам це потрібно, якщо у вас є a, typedefякий визначає багато змінних, які ви обробляєте по-різному, залежно від їх типу (наприклад, серіалізація). Ви можете не знати, яка з цих змінних залишиться в майбутніх версіях коду, тому вибір типу в якості аргументу є логічно правильним. Навіть зміна таких typedefне вплине на ваш розмір лінії.


-1

Залежно від розміру цілі (змінна) може бути найкращим рішенням. Таким чином, ви можете змінити тип змінної за необхідності, не виправляючи всі записи для типу. Але знову ж таки, це залежить від вашої мети.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.