Как инициализировать структуру в соответствии со стандартами языка программирования C

466

Я хочу инициализировать элемент структуры, разделить в декларации и инициализации. Вот что у меня есть:

typedef struct MY_TYPE {
  bool flag;
  short int value;
  double stuff;
} MY_TYPE;

void function(void) {
  MY_TYPE a;
  ...
  a = { true, 15, 0.123 }
}

Это способ объявить и инициализировать локальную переменную MY_TYPEв соответствии со стандартами языка программирования C (C89, C90, C99, C11 и т. Д.)? Или есть что-нибудь лучше или хотя бы работает?

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

пресмыкаться
источник
3
Вы действительно должны принять лучший ответ, я вижу, что вам пришлось использовать какое-то плохое руководство по кодированию, но вы все равно не должны предлагать другим людям, что это правильный способ сделать это ...
Кароли Хорват
@KarolyHorvath хорошо, большинство хороших ответов относятся к C99. Может быть, мой вопрос является дубликатом stackoverflow.com/questions/6624975/… ?
съеживаться
если это было ваше первоначальное намерение, то, вероятно, да, но тогда 1) голоса были бы очень вводящими в заблуждение. 2) из ​​самых популярных поисковых запросов это единственный, который показывает путь C99 ..... было бы лучше повторно использовать эту страницу для демонстрации C99 ... (очевидно, люди начали ссылаться на эту страницу, чтобы показать, как сделай это)
Кароли Хорват
10
Интересно, что принятый (и сильно одобренный) ответ на самом деле не отвечает на вопрос, даже если он был опубликован изначально. Назначенные инициализаторы не решают проблему OP, которая состоит в том, чтобы отделить объявление от инициализации. Для C до 1999 года единственным реальным решением является назначение каждому члену; для C99 и позже, составной литерал, как в ответе CesarB, является решением. (Конечно, фактический инициализатор, с указателями или без них, был бы даже лучше, но, очевидно, OP был обременен действительно плохим стандартом кодирования.)
Кейт Томпсон,
32
Строго говоря, термин «ANSI C» теперь относится к стандарту ISO 2011 года, который был принят ANSI. Но на практике термин «ANSI C» обычно относится к (официально устаревшему) стандарту 1989 года. Например, «gcc -ansi» все еще применяет стандарт 1989 года. Поскольку именно ИСО опубликовала стандарты 1990, 1999 и 2011 годов, лучше избегать термина «ANSI C» и указывать дату стандарта, если есть какая-либо вероятность путаницы.
Кит Томпсон

Ответы:

728

В (ANSI) C99 вы можете использовать назначенный инициализатор для инициализации структуры:

MY_TYPE a = { .flag = true, .value = 123, .stuff = 0.456 };

Редактировать: другие члены инициализируются как ноль: «пропущенные члены поля неявно инициализируются так же, как объекты, которые имеют статическую продолжительность хранения». ( https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html )

philant
источник
87
офигенно спасибо Вы можете даже вкладывать их в себя: static oxeRay2f ray = {.unitDir = {.x = 1.0f, .y = 0.0f}};
Орион Элензил
4
Это не отвечает на вопрос вообще. ОП хочет отделить инициализацию от декларации.
osvein
3
C99! = ANSI C - так что это не работает ни в C89, ни в C90.
Тим Виссманн
3
C99 - это ANSI C, смотрите это
Cutberto Ocampo
3
@CutbertoOcampo Я понимаю, что случилось сейчас. Первоначальный вопрос заключался в поиске решений в ANSI C, очевидно, он хотел получить ответы только для C89. В 2016 году вопрос был отредактирован и удалено «ANSI C», что затрудняет понимание того, почему в этом ответе и комментариях упоминается «(ANSI) C99».
Этьен
198

Вы можете сделать это с составным литералом . Согласно этой странице, он работает в C99 (который также считается ANSI C ).

MY_TYPE a;

a = (MY_TYPE) { .flag = true, .value = 123, .stuff = 0.456 };
...
a = (MY_TYPE) { .value = 234, .stuff = 1.234, .flag = false };

Обозначения в инициализаторах являются необязательными; Вы также можете написать:

a = (MY_TYPE) { true,  123, 0.456 };
...
a = (MY_TYPE) { false, 234, 1.234 };
CesarB
источник
Итак, назначенные инициализаторы предназначены для того, когда вы объявляете и определяете одновременно, а составные литералы - для того, когда вы определяете уже объявленную переменную?
Геремия
@ Geremia, вы должны сначала определить структуру в обоих случаях, но с помощью назначенных инициализаторов вы сделаете код более читабельным и более устойчивым к ошибкам в случае изменений в структуре.
Hoijui
2
Это немного сложно. Много раз (некоторые успешные) я пытался использовать назначенные инициализаторы. Однако, только теперь стало ясно (благодаря примеру вашего и @ philant), что должен существовать приведение, если инициализатор не используется во время создания объекта. Однако теперь я также узнал, что если инициализаторы используются не во время создания объекта (как в вашем примере), тогда он называется составным литералом , а не строго указанным инициализатором . ideone.com/Ze3rnZ
sherrellbc
93

Я вижу, что вы уже получили ответ об ANSI C 99, поэтому я расскажу о ANSI C 89. ANSI C 89 позволяет вам инициализировать структуру следующим образом:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = { 5, 2.2, "George" };
    return 0;
}

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

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

Удачи!

Рон Нуни
источник
3
Можно ли использовать переменные при инициализации?
Cyan
2
@Cyan Это для объектов с автоматическим сроком хранения. См. 6.7.9 13) в open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf . Для глобальных объектов в значительной степени ограничивается литералами. Вы даже не можете использовать другие глобальные объекты, даже если они постоянны.
PSkocik
Таким образом, единственная разница с C 99 в том, что вы не можете указать обозначения?
Akaisteph7
42

a = (MYTYPE){ true, 15, 0.123 };

будет хорошо в C99

qrdl
источник
1
Честно говоря, я думаю, что ответ самый полезный, но неважно.
user12211554
22

Вы почти получили это ...

MY_TYPE a = { true,15,0.123 };

Быстрый поиск по 'struct initialize c' показывает мне это

Kieveli
источник
1
Я думаю, что это верно для стандарта C, но не для ANSI C (99?). Кроме того, я связан с правилами кодирования, которые не позволяют мне одновременно выполнять декларирование и инициализацию, поэтому я должен разделить его и инициализировать каждый отдельный подэлемент.
съеживаться
8
Инициализация может произойти только в точке объявления. Вот что значит «инициализировать». В противном случае вы позволяете неопределенному значению быть начальным значением вашей переменной / структуры, а затем присваиваете значение позже.
Kieveli
8
эм ... У вас есть правила кодирования, которые намеренно плохие? Возможно, вам следует представить парня, который написал их для «Приобретение ресурсов - инициализация» ( en.wikipedia.org/wiki/RAII )
Джеймс Керран,
Поверьте мне, я действительно, действительно, не могу немного изменить эти правила на данный момент. Это ужасно, кодировать это. И есть много других правил прямо из 70-х, например, статические заголовки кода с управляющей информацией, такой как номер редакции. Изменения для каждого выпуска, даже если источник не изменился ...
съеживайтесь
19

Стандарт языка программирования C ISO / IEC 9899: 1999 (обычно известный как C99) позволяет использовать назначенный инициализатор для инициализации членов структуры или объединения следующим образом:

MY_TYPE a = { .stuff = 0.456, .flag = true, .value = 123 };

Он определен в paragraph 7разделе 6.7.8 Initializationстандарта ISO / IEC 9899: 1999 как:

Если обозначение имеет форму
. идентификатор,
тогда текущий объект (определенный ниже) должен иметь структуру или тип объединения, а идентификатор должен быть именем члена этого типа.

Обратите внимание, что paragraph 9в том же разделе говорится, что:

За исключением случаев, когда явно указано иное, для целей настоящего подпункта неназванные члены объектов структуры и типа объединения не участвуют в инициализации. Безымянные члены объектов структуры имеют неопределенное значение даже после инициализации.

Однако в реализации GNU GCC пропущенные члены инициализируются как нулевое или подобное нулю значение, соответствующее типу. Как указано в разделе 6.27 Назначенные инициализаторы документации GNU GCC:

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

Компилятор Microsoft Visual C ++ должен поддерживать назначенные инициализаторы с версии 2013, согласно официальному сообщению в блоге C ++ Conformance Roadmap . Пункт Initializing unions and structsиз Инициализаторы статьи в MSDN визуальной документации студии предполагает , что неназванные члены инициализируются нулевыми, как соответствующие значения аналогично GNU GCC.

Стандарт ISO / IEC 9899: 2011 (широко известный как C11), который заменил ISO / IEC 9899: 1999, сохраняет обозначенные инициализаторы в разделе 6.7.9 Initialization. Это также сохраняетparagraph 9 без изменений.

Новый стандарт ISO / IEC 9899: 2018 (обычно известный как C18), который заменил ISO / IEC 9899: 2011, сохраняет обозначенные инициализаторы в разделе 6.7.9 Initialization. Также сохраняется paragraph 9без изменений.

PF4Public
источник
3
Первоначально я предложил это как правку принятому ответу, но он был отклонен. Таким образом я отправляю это как ответ.
PF4Public
Я считаю, что «Безымянный член» означает не «пропущенные поля», а скорее «анонимные члены», такие как union in struct thing { union { char ch; int i; }; };. Позднее стандарт гласит: «Если в списке, заключенном в фигурные скобки, меньше инициализаторов, чем элементов или членов агрегата, или меньше символов в строковом литерале, используемом для инициализации массива известного размера, чем элементов в массиве, остальная часть совокупности должна быть инициализирована неявно так же, как объекты, которые имеют статическую продолжительность хранения. "
всплытие
14

как сказал Рон Нуни:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = {5, 2.2, "George"};
    return 0;
}

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

Если вы не инициализируете значения в своей структуре (т.е. если вы просто объявляете эту переменную), все variable.membersбудут содержать «мусорные значения», только если объявление является локальным!

Если объявление является глобальным или статическим (как в этом случае), все неинициализированные variable.membersбудут автоматически инициализированы следующим образом:

  • 0 для целых чисел и с плавающей точкой
  • '\0'для char(конечно, это так же, как 0иchar целочисленный тип)
  • NULL для указателей.
r_goyal
источник
Интересный к локальному / глобальному, это также относится к значениям структуры времени. У меня был местный, и в одном случае DST был включен, в другом - нет, не из-за того, что я сделал. Я не использовал DST (поле tm_isdst), это было из strptime, но когда я конвертировал в time_t, у некоторых был час отдыха.
Алан Кори
3
void function(void) {
  MY_TYPE a;
  a.flag = true;
  a.value = 15;
  a.stuff = 0.123;
}
Роберт
источник
25
как только я услышал «инициализация отличается от назначения». так это не назначение, а не инициализация?
Хайри Угур Колтук
2

Если MS не обновилась до C99, MY_TYPE a = {true, 15,0.123};

eddyq
источник
2

Я нашел другой способ инициализации структур.

Структура:

typedef struct test {
    int num;
    char* str;
} test;

Инициализация:

test tt = {
    num: 42,
    str: "nice"
};

Согласно документации GCC, этот синтаксис устарел с GCC 2.5 .

MichaelWang
источник
2

Мне не понравился ни один из этих ответов, поэтому я сделал свой собственный. Я не знаю, является ли это ANSI C или нет, это просто GCC 4.2.1 в режиме по умолчанию. Я никогда не могу вспомнить скобки, поэтому я начинаю с подмножества моих данных и сражаюсь с сообщениями об ошибках компилятора, пока они не закроются. Читаемость является моим первым приоритетом.

    // in a header:
    typedef unsigned char uchar;

    struct fields {
      uchar num;
      uchar lbl[35];
    };

    // in an actual c file (I have 2 in this case)
    struct fields labels[] = {
      {0,"Package"},
      {1,"Version"},
      {2,"Apport"},
      {3,"Architecture"},
      {4,"Bugs"},
      {5,"Description-md5"},
      {6,"Essential"},
      {7,"Filename"},
      {8,"Ghc-Package"},
      {9,"Gstreamer-Version"},
      {10,"Homepage"},
      {11,"Installed-Size"},
      {12,"MD5sum"},
      {13,"Maintainer"},
      {14,"Modaliases"},
      {15,"Multi-Arch"},
      {16,"Npp-Description"},
      {17,"Npp-File"},
      {18,"Npp-Name"},
      {19,"Origin"}
    };

Данные могут начинать жизнь как файл с разделителями табуляцией, который вы ищете и заменяете, чтобы преобразовать его во что-то другое. Да, это вещи Debian. Итак, одна внешняя пара {} (с указанием массива), затем другая пара для каждой структуры внутри. С запятыми между. Помещать вещи в заголовок не обязательно, но в моей структуре около 50 элементов, поэтому я хочу, чтобы они были в отдельном файле, чтобы не допустить путаницы в моем коде, и поэтому их легче заменить.

Алан Кори
источник
3
Не было и речи об инициализации массива структур! Я имею в виду, ваш ответ содержит накладные расходы.
Виктор Сигнаевский
Я предполагаю, что моей целью было объявление структуры со значениями уже в ней, а не использование = для установки каждого значения позже.
Алан Кори
Этот принцип применяется, будь то одна структура или массив из них. Использование = на самом деле не инициализация, я бы не подумал.
Алан Кори
0

Структура в C может быть объявлена ​​и инициализирована следующим образом:

typedef struct book
{
    char title[10];
    char author[10];
    float price;
} book;

int main() {
    book b1={"DS", "Ajay", 250.0};

    printf("%s \t %s \t %f", b1.title, b1.author, b1.price);

    return 0;
}
Аджай Парашар
источник
0

Я прочитал документацию по Microsoft Visual Studio 2015 для инициализации составных типов , все формы инициализации с{...} там объясняются , но там не упоминается инициализация с помощью точки с именем '' указатель ''. Это не работает также.

Глава C.7 стандарта 6.7.8 Инициализация объясняет возможность указателей, но, на мой взгляд, она не совсем понятна для сложных структур. Стандарт C99 в формате PDF .

На мой взгляд, может быть, лучше

  1. Использовать = {0}; -initialization для всех статических данных. Это меньше усилий для машинного кода.
  2. Используйте макросы для инициализации, например

    typedef MyStruct_t{ int x, int a, int b; } MyStruct; define INIT_MyStruct(A,B) { 0, A, B}

Макрос может быть адаптирован, его список аргументов может быть независимым от измененного содержимого структуры. Это правильно, если нужно инициализировать меньше элементов. Это также подходит для вложенной структуры. 3. Простая форма: инициализация в подпрограмме:

void init_MyStruct(MyStruct* thiz, int a, int b) {
  thiz->a = a; thiz->b = b; }

Эта подпрограмма выглядит как ObjectOriented в C. Используйте thiz, но и не thisкомпилируйте ее с C ++!

MyStruct data = {0}; //all is zero!
init_MyStruct(&data, 3, 456);
Хартмут Шорриг
источник
0

Я искал хороший способ инициализации моей структуры, и я должен использовать ниже (C99). Это позволяет мне инициализировать либо одну структуру, либо массив структур так же, как обычные типы.

typedef struct {
    char *str;
    size_t len;
    jsmntok_t *tok;
    int tsz;
} jsmn_ts;

#define jsmn_ts_default (jsmn_ts){NULL, 0, NULL, 0}

Это может быть использовано в коде как:

jsmn_ts mydata = jsmn_ts_default; /* initialization of a single struct */

jsmn_ts myarray[10] = {jsmn_ts_default, jsmn_ts_default}; /* initialization of
                                                    first 2 structs in the array */
div0man
источник
Но это не инициализирует массив структур значениями по умолчанию. Он инициализирует первую структуру в массиве значениями, указанными в jsmn_ts_default, но остальные структуры инициализируются так, как если бы массив имел статическую продолжительность хранения, что означает, что члены инициализируются в NULLи 0. Но если вы #define jsmn_ts_default (jsmn_ts){"default", sizeof "default", NULL, 0}перейдете к нему, вы увидите, что только первый элемент массива инициализируется.
ex nihilo
Да, я только что вернулся с тестирования, хотел отредактировать пост :) Кстати, такое же поведение происходит int a[10] = {1};только с 1-м элементом==1
div0man