Для структурной переменной, почему инициализатор {21,19,3.6} такой же, как {{21,19}, 3.6}, но не наоборот?

В следующем примере я проиллюстрировал это, используя две структуры test1 и test2 . Первый имеет два элемента: целочисленный массив размером два и элемент float. Вторая структура имеет 3 элемента, 2 целых числа и один поплавок.

Я инициализирую две структурные переменные s1 и s2 для test1 как:

 s1={{23,52},2.5},s2={21,19,3.6}; 

Оба работают нормально, хотя для s2 я вынул фигурные скобки, которые заключают в себе элементы массива. Он отлично работает без предупреждения, а вывод корректен. Но когда я инициализирую 2 переменные для test2 следующим образом:

  v1={{23,52},2.5},v2={21,19,3.6}; 

Я получаю неправильный вывод, когда я пытаюсь распечатать значения v1, и это предупреждения, которые я получил при компиляции:

 warning: braces around scalar initializer| warning: (near initialization for 'v1.list1')| warning: excess elements in scalar initializer| warning: (near initialization for 'v1.list1')| ||=== Build finished: 0 errors, 4 warnings ===| 

Исходя из этой предпосылки, пожалуйста, проясните следующие сомнения, которые возникают:

Вопрос: Если использование v1={{23,52},2.5} вместо v1={23,52,2.5} путает компилятор о том, являются ли первые 2 числа отличными целочисленными элементами структуры или части элемента целочисленного массива структура, то почему не использует s2={21,19,3.6} вместо s2={{21,19},3.6} путать компилятор, думая, что структура varialbe s2 имеет 3 элемента (2 целых элемента и один float) вместо 2-х элементов (один целочисленный массив размером 2 и поплавка)? Что я особенно хочу понять, так почему первый случай о инициализации v1 ошибочен.

 #include struct test1{ int list[2]; float rate; }s1={{23,52},2.5},s2={21,19,3.6}; //Works fine struct test2{ int list1; int list2; float rate; }v1={{23,52},2.5},v2={21,19,3.6}; //Messes things up int main(void) { printf("%d,%d,%f\n",s1.list[1],s2.list[1],s2.rate); printf("%d,%d,%f\n",v1.list1,v1.list2,v1.rate); } 

Это просто следствие того, как определяются правила для инициализаторов. Если текущий объект инициализации является struct , union или массивом, то, если следующий инициализатор начинается с a { тогда инициализаторы, заключенные в эту скобку и его соответствие } , используются для инициализации членов этого объекта; в противном случае он просто марширует через список инициализаторов, принимая столько, сколько ему нужно.

Итак, в первом случае s1={{23,52},2.5} , текущий объект начинается как s1.list . Это массив, а следующий инициализатор – { 23, 52 } , поэтому он используется для инициализации массива. s1.rate теперь является текущим объектом, а следующий инициализатор равен 2.5 , поэтому работает так, как ожидалось.

Во втором случае s2={21,19,3.6} текущий объект начинается как s2.list . Это массив, но следующий инициализатор не запускается с { – поэтому он принимает столько значений, сколько ему нужно (в данном случае, два), и инициализирует массив с 21 и 19 . s2.rate теперь является текущим объектом, а следующий инициализатор равен 2.5 , поэтому снова работает так, как ожидалось.

В третьем случае v1={{23,52},2.5} , текущий объект начинается как v1.list1 . Это скаляр, а соответствующий инициализатор – {23, 52} . Это нарушает ограничение языка – «Инициализатор для скаляра должен быть одним выражением, необязательно заключенным в фигурные скобки » – поэтому вы получаете предупреждение. Формально поведение вашей программы не определено, но похоже, что ваш компилятор просто использует первое значение, содержащееся в инициализаторе, и отбрасывает лишние. Текущий объект теперь v1.list2 , а следующий инициализатор – 2.5 , поэтому для инициализации этого элемента используется неправильное значение. Для v1.rate нет инициализатора; поскольку v1 имеет статическую продолжительность хранения, этот член инициализирует 0.0 .

В четвертом случае v2={21,19,3.6} текущий объект начинается как v1.list1 , а следующий инициализатор равен 21 – это значение используется для инициализации члена. После этого текущий объект – v1.list2 а следующий инициализатор – 19 ; то v1.rate – текущий объект, а следующий инициализатор – 3.6 .

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

В случае переменной s2 компилятор знает размер встроенного массива, поэтому он может правильно назначать его даже без наручников. вы можете, однако, получить предупреждение о том, что вы можете использовать наручники, и если вы этого не сделаете, я предлагаю вам включить больше предупреждений компилятора (полезно исправить предупреждения, так как они могут указывать на ошибки, которые могут быть допустимыми C, но недействительная логика).

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