Який розмір порожнечі?


77

Що дасть це твердження?

void *p = malloc(sizeof(void));

Редагувати: розширення запитання.

Якщо sizeof (void) дає 1 у компіляторі GCC, то виділяється 1 байт пам'яті, і вказівник p вказує на цей байт, а p ++ буде збільшено до 0x2346? Нехай p дорівнює 0x2345. Я говорю про p, а не * p.


4
FYI. Це нестандартно C. У багатьох компіляторах це буде помилкою під час компіляції.
Йохан Котлінський

чому ти повинен знати. Розуміння цього може допомогти нам відповісти на питання.
Мартін Йорк,

4
Коментуючи ваше редагування: так, у GCC (лише), збільшення порожнього покажчика додає одиницю до значення. Якщо ви цінуєте переносимість свого коду, не зловживайте свободою, яку дає вам GCC. Це абсолютно нестандартно. І GCC визнає стільки ж із '-std = c99 -pedantic'.
Джонатан Леффлер

1
Пропозиція щодо регулярних порожнеч: open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0146r1.html
alfC

Відповіді:


56

Тип voidне має розміру; це була б помилка компіляції. З тієї ж причини ви не можете зробити щось на зразок:

void n;

РЕДАГУВАТИ. На мій подив, sizeof(void)фактично відбувається компіляція в GNU C:

$ echo 'int main() { printf("%d", sizeof(void)); }' | gcc -xc -w - && ./a.out 
1

Однак у C ++ це не робить:

$ echo 'int main() { printf("%d", sizeof(void)); }' | gcc -xc++ -w - && ./a.out 
<stdin>: In function 'int main()':
<stdin>:1: error: invalid application of 'sizeof' to a void type
<stdin>:1: error: 'printf' was not declared in this scope

31
Але GCC має можливість обробити 'sizeof (void) == sizeof (char)' ... Див. -Wpointer-arith
Джонатан Леффлер

5
@Jon це для дурнів з початку 80-х, які тільки починали вчитися програмуванню. Стандарти широко не приймались.
unixman83

5
sizeof (void)є порушенням обмеження. Відповідний компілятор C (який gcc не є за замовчуванням) повинен принаймні попередити про це і може (а IMHO повинен) відхилити його. Довідково: N1570 6.5.3.4p1 ( voidє неповним типом). Це було так від стандарту 1989 року, який був введений void.
Кіт Томпсон,

3
@ unixman83: Це не має сенсу. voidне існував до того, як його запровадив стандарт ANSI C 1989 р., той самий стандарт, який sizeof (void)допускав порушення обмежень.
Кіт Томпсон,

1
Sizeof void компілюється, оскільки він потрібен для роботи з покажчиками void *.
kelin

41

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

Загалом, voidце неповний тип, і ви не можете використовувати sizeof для неповних типів.


2
Варіант -Wpointer-arithмається на увазі -pedantic. Я завжди користувався -pedantic. :)
Джош Лі

1
@jleedev Справді, я також використовую -педант, наскільки можу. На жаль, однак деякі заголовки сторонніх бібліотек роблять gcc -педанта дуже сумним :(
hrnt

1
+1 Дійсно, чому GCC має тисячі марних розширень?

@ user142019: тому що вони корисні? І допомагає отримати послідовну модель бекенда за допомогою різних мов програмування та апаратної архітектури.
kriss

16

Хоча voidможе стояти на місці для типу, він насправді не може містити значення. Тому він не має розміру в пам'яті. Отримання розміру a voidне визначено.

void Покажчик просто конструкція мови означає покажчик на нетипізованого пам'яті.


1
і що ? Чи не повинна будь-яка порожня структура також споживати ніякого розміру в пам'яті, і це має бути проблемою. Ну насправді мені цікаво, чи порожні структури справді використовують 0 байт в C ++ (як цеправда в C), виглядає як sizeof повертає 1 для них.
kriss

1
@kriss Я не впевнений , що ви говорите , але як ви самі помітили, порожні Структури робити займають пам'ять через ідентичності об'єкта.
Конрад Рудольф

1
справді, але це не було правдою в C, і використання пам'яті для порожніх предметів відчуває зло.
kriss

13

voidне має розміру. І в C, і в C ++ вираз sizeof (void)є недійсним.

У C, цитуючи пункт 1 N1570 6.5.3.4:

sizeofОператор не повинен бути застосований до вираження , яке має тип функції або неповний тип , в дужках імені такого типу, або вираз , яке позначає елемент бітового поля.

(N1570 - це проект стандарту ISO C 2011 р.)

voidє неповним типом. Цей параграф є обмеженням , що означає, що будь-який відповідний компілятор С повинен діагностувати будь-яке його порушення. (Діагностичне повідомлення може бути не смертельним попередженням.)

Стандарт С ++ 11 має дуже подібне формулювання. Обидва видання були опубліковані після того, як було задано це питання, але правила повертаються до стандарту ANSI C 1989 року та найдавніших стандартів C ++. Насправді правило, яке voidє неповним типом, до якого sizeofможе не застосовуватися, сягає рівно стільки, скільки введення voidв мову.

gcc має розширення, яке розглядається sizeof (void)як 1. gcc не є відповідним компілятором C за замовчуванням, тому в режимі за замовчуванням він не попереджає про це sizeof (void). Такі розширення дозволені навіть для повністю відповідних компіляторів С, але діагностика все одно потрібна.


Я припускаю, що вони допустили sizeof(void) == 1узгодженість з арифметикою voidпокажчика на покажчику, тобто також розширенням gcc . Наводячи з їх документації: A consequence of this is that sizeof is also allowed on void and on function types, and returns 1.. Але все-таки діагностичне повідомлення повинно бути за замовчуванням для C, оскільки Standard вимагає цього.
Grzegorz Szpetkowski

2
@GrzegorzSzpetkowski: (Відповідь через пару років.) Так, стандарт вимагає діагностики, але gcc за замовчуванням не відповідає компілятору C. Логічно, що стандарт C не встановлює жодних вимог до реалізацій, які не претендують на відповідність йому. gcc можна зробити (майже майже) відповідним правильним параметрам командного рядка, таким як -std=c11 -pedantic. З такими опціями він видає необхідну діагностику.
Кіт Томпсон,


6

sizeof()не можна застосовувати до неповних типів. І voidє неповним типом, який неможливо завершити.


3

У С, sizeof(void) == 1 в GCC, але, схоже, це залежить від вашого компілятора.

У C ++ я отримую:

У функції 'int main ()':
Рядок 2: помилка: недійсне застосування 'sizeof' до типу void
компіляція припинена через -Wfatal-помилки.

3
Тільки в GCC розмір (void) == 1; у стандарті це невизначена поведінка.
Джонатан Леффлер

Оновлено, щоб додати примітку щодо GCC.
Грег Хьюгілл,

2
@JonathanLeffler: Це не просто невизначена поведінка. Це порушення обмеження. N1570 6.5.3.4p1.
Кіт Томпсон,

Це не тільки GCC, це GCC та будь-які інші компілятори, призначені для сумісності з ним (включаючи clang, icc, tcc та, можливо, інші).
Кіт Томпсон,

0

До 2-ї частини запитання: Зверніть увагу, що sizeof (void *)! = Sizeof (void). На 32-бітовій арці sizeof (void *) дорівнює 4 байтам, тому p ++ буде встановлено відповідно. Величина, на яку збільшується покажчик, залежить від даних, на які він вказує. Отже, він буде збільшений на 1 байт.


1
так p вказував на 1 байт даних, оскільки sizeof (void) дає 1, тому p ++ означає, що p буде збільшено на 1, а не на 4 ??
Лакшмі

1
Сума, на яку збільшується покажчик, залежить від даних, на які він вказує. Так що так.

p типу void * (покажчик void), а не void. Отже, він буде збільшуватися на розмір типу void * (який зазвичай становить 4 у 32-розрядних системах).
Technowise

4
@Technowise: ні. Будь-який покажчик типу T*збільшується на sizeof(T)не sizeof(T*) , що майже завжди буде однаковим, за винятком, можливо, покажчиків на функції), коли він збільшується. Звичайно, виняток void(і знову ж виняток - якщо ввімкнено розширення GCC).
Конрад Рудольф

1
@Amit: Як ви дійшли висновку, що розмір даних, на які void*вказує вказівник, становить 1 байт? sizeof (void)є порушенням обмеження. (Розширення gcc розглядає це як 1.)
Кіт Томпсон,

-1

хоча sizeof (void), можливо, не має сенсу сам по собі, це важливо, коли ви робите будь-яку математику покажчика.

напр.

 void *p;
 while(...)
      p++;

Якщо sizeof (void) вважається 1, це буде працювати. Якщо sizeof (void) вважається 0, ви потрапляєте в нескінченний цикл.


4
Приклад фрагмента коду є незаконним у C та C ++. Деякі компілятори все одно дозволяють.
Джеймс Рот,

1
Ні, це зовсім не важливо (хоча автори gcc, мабуть, думали, що це було). Якщо ви хочете зробити арифметику покажчиків на байтових вказівниках, використовуйте unsigned char*.
Кіт Томпсон,

@KeithThompson - навіть charне гарантовано 1 байт. Я працював над системою, яка встановила для нього 32 біти.
зола

@ash: Так, charгарантовано становить 1 байт. Якщо ви працювали в системі з 32-розрядною версією char, тоді байт у цій системі становить 32 біти ( CHAR_BIT == 32). Ось як стандарт С визначає "байт". Стандарт С, 6.5.3.4 пункти 2 і 4: « sizeofОператор дає розмір (в байтах) свій операнд [...] Коли. sizeofЗастосовуються до операнду , який має тип char, unsigned charабо signed char, (або кваліфіковану версії їх) результат - 1. "
Кіт Томпсон,

Коли вийшов стандарт C 6.5.3.4? Я працював над цією системою ще в середині 90-х.
ясен

-1

Більшість компіляторів C ++ вирішили викликати помилку компіляції при спробі отримати sizeof(void) .

Під час компіляції C gcc не відповідає нормам і вирішив визначити sizeof(void)як 1. Це може виглядати дивно, але має обґрунтування. Коли ви робите арифметичне вказівника, додавання або видалення однієї одиниці означає додавання або видалення об'єкта, вказаного на розмір. Таким чином, визначення sizeof(void)як 1 допомагає визначити void*як вказівник на байт (нетипізована адреса пам'яті). В іншому випадку у вас буде дивовижна поведінка з використанням арифметики вказівникаp+1 == p when p isvoid* . Така арифметика покажчиків на порожніх вказівниках не допускається в C ++, але чудово працює із компіляцією C з gcc.

Стандартним рекомендованим способом буде використання char* для такої мети (вказівник на байт).

Ще одна подібна різниця між C та C ++ при використанні sizeof виникає, коли ви визначили порожню структуру, наприклад:

struct Empty {
} empty;

Використання gcc як мого компілятора C. sizeof(empty) повертає 0. За допомогою g ++ той самий код поверне 1.

Я не впевнений, що зазначає стандарти C і C ++ щодо цього питання, але я вважаю, що визначення розміру деяких порожніх структур / об'єктів допомагає в управлінні посиланнями, щоб уникнути того, що два посилання на різні послідовні об'єкти, перше порожнє, отримують та ж адреса. Якщо посилання реалізуються з використанням прихованих покажчиків, як це часто роблять, переконавшись, що інша адреса допоможе їх порівняти.

Але це лише уникнення дивовижної поведінки (порівняння посилань у кутових випадках) шляхом введення іншого (порожні об'єкти, навіть POD споживають принаймні 1 байт пам'яті).


6
"Натомість C вибрав розмір (void) як 1". - Ні, це неправильно. sizeof(void)не визначено в С, так само, як у С ++. GCC просто не відповідає цьому пункту, а арифметика voidпокажчиків на покажчиках також просто не визначена - void* p; p++;недійсна. Навіть GCC дасть вам попередження під час складання -pedanticпрапора. І ви також помиляєтесь щодо поведінки GCC за замовчуванням для C ++: за замовчуванням це трактується як помилка .
Конрад Рудольф

@Konrad: так, я цілком впевнений, що це не було правдою зі старою версією gcc, але сучасні роблять, як ти кажеш.
kriss

Принаймні в C пусте structвизначення - це синтаксична помилка; Підтримка gcc - це розширення. Вони можуть бути дійсними в C ++; Я не впевнений.
Кіт Томпсон,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.