Чи має значення порядок членів у структурі?


77

Я виявив особливу поведінку в C. Розглянемо наведений нижче код:

Він компілюється просто чудово. Потім змініть порядок членів struct zтаким чином

І раптом ми отримуємо помилку компіляції field has incomplete type 'struct s []'.

Чому так?

Відповіді:


91

Порядок полів у a structмає значення - компілятору не дозволяється змінювати порядок полів, тому розмір structможе змінюватися в результаті додавання деяких відступів.

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

  • Ніколи не може бути більше одного такого члена,
  • Якщо присутні, гнучкий елемент повинен бути останнім в structі
  • На structдодаток до гнучкого повинен бути принаймні один член.

Погляньте на ці запитання, щоб отримати невелику ілюстрацію використання гнучких елементів конструкції.


5
Тоді здається неймовірно поганою діагностикою (хоча і технічно точною).
Гонки легкості на орбіті

5
@LightnessRacesinOrbit Ви маєте рацію, мені також не дуже подобається повідомлення про помилку.
Сергій Калініченко

21

Компілятор не може розрахувати, скільки пам’яті struct s b[];буде зайнято. Це означає, що якщо структура має будь-які поля після неї, компілятор не може зрозуміти, де ці поля.

Раніше (у старих версіях C) (наприклад) struct s b[];не дозволялося входити до складу структури. Це робило ефективне управління пам’яттю неприємним. Для простого прикладу уявіть, що у вас є структура, що містить рядок "name" (це може бути лише кілька символів або багато з них). Ви можете використовувати масив фіксованого розміру, який є достатньо великим для найбільшого імені (що марнує простір), або скористатися покажчиком та виділити 2 частини пам'яті (одну для структури та одну для рядка імені змінної довжини). Крім того, ви можете використовувати вказівник і зробити так, щоб він вказував на додатковий простір за кінцем структури, що закінчується приблизно так:

Це був найефективніший варіант; але це також негарно та схильне до помилок.

Щоб обійти це, компілятори додали нестандартні розширення, щоб дозволити "масиви невідомого розміру" в кінці структури. Це трохи полегшило програмістів і зробило трохи більш ефективним (оскільки немає необхідності в додатковому члені покажчика). Врешті-решт це було прийнято стандартом С (можливо, в С99 - я не пам’ятаю).


2
Цікаво, що до того, як гнучкі члени масиву стали доступними як стандарт, я згадую використання деяких компіляторів, які (чи то за проектом, чи випадково) дозволяли коду оголошувати масив розміром нуль як частину структури, ймовірно, тому, що опускання перевірки розміру нульового масиву призведе до того, що такі масиви будуть дозволені за замовчуванням. Використання порожнього []дещо відрізняється семантично, оскільки компілятор дозволить безпосереднє створення структури, що містить масив нульового розміру (хоча використання масиву має бути UB), тоді як FLA або структура, що містить один, має бути останнім елементом структура.
supercat

@supercat: Так, масиви нульової довжини були введені як розширення в GCC, принаймні, версією 2 ( gcc.gnu.org/onlinedocs/gcc-2.95.3/gcc_4.html#SEC74 ), і вони підтримуються навіть зараз ( gcc.gnu.org/onlinedocs/gcc/Zero-Length.html ).
Немо

15

Порядок членів зазвичай має значення (тобто між полями може бути вставлено якесь заповнення), але у вашому конкретному випадку ви використовуєте гнучкий масив членів , це стандартизовано в C99 - 6.7.2.1.16

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

Ваш struct s b[];член призначений для використання для доступу до динамічних розподілів купи для декількох struct sелементів.


1
Дякуємо за цитування стандарту. Я часто замислювався, який розмір буде така конструкція.
Дан

4

Назва вашого запитання: "Чи порядок членів у structпитанні?".

Очевидна проблема у вашому коді пов'язана з тим, що ваш structмістить гнучкий член.

Отже, тут є додаткове питання, пов’язане із загальним питанням порядку членів у struct:


Візьмемо для прикладу наступні дві структури:

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

Тому в struct s2кінцевому підсумку може скомпілюватись у

Зрештою це призведе до різного розміру для примірників типів struct s1та struct s2.


1

Порядок має значення У ЦЬОМУ СПРАВІ. Ваш struct zмістить масив, що складається з structs s. Однак цей масив не має розміру, пов'язаного з ним, тому компілятор не знає, як розподілити відповідний простір стека, оскільки потім є інше поле struct ( int a). Приклад того, як це МОЖЕ працювати:

Якщо вам дійсно потрібен масив для зміни розміру, найкраще, якщо ви виділите всю структуру в купі з масивом як вказівник, struct sа потім динамічно перерозподілите його для розміру змінюваного розміру масиву. Подивіться malloc (3), realloc 3), calloc (3), і free (3).

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