Что представляет собой агрегатная инициализация

В разделе "Инициализация массива" в главе 4, стр. 231 "Мышление в Java, 2-е издание" говорится следующее:

Инициализация массивов в C подвержена ошибкам и утомительна. C++ использует агрегатную инициализацию, чтобы сделать ее намного более безопасной. В Java нет таких "агрегатов", как C++, поскольку в Java все является объектом. У него есть массивы, и они поддерживаются с инициализацией массива.

Почему это подвержено ошибкам и утомительно в C? Что это означает под агрегатной инициализацией и почему она безопаснее? Я натолкнулся на главу "Совокупная инициализация" Брюса Экеля "Мышление в C++" (2-е изд.), Но она ни в чем меня не убеждает.

Ответы

Ответ 1

Прежде всего, чтобы ответить на главный вопрос, агрегированная инициализация означает использование списков инициализаторов, заключенных в фигурные скобки, для инициализации всех членов агрегата (т.е. массив или структура [на С++, только определенные типы структур считаются агрегатами]).

Очевидно, что

int ar[] = { 1 , 2 };

безопаснее, чем

int ar[2];
ar[0] = 1;
ar[1] = 2;

поскольку последний дает широкие возможности для опечаток и других ошибок в индексах отдельных элементов для инициализации.

Глядя сегодня на C и С++, мне непонятно, почему автор делает различие между C и С++. Оба языка включают агрегатную инициализацию для массивов.

Одна из возможностей заключается в том, что автор ссылался на старые версии стандарта C. Примечательно, что в ANSI C (C89) важное ограничение, применяемое к использованию инициализации агрегата: все инициализаторы должны были быть постоянными выражениями:

/* This is possible in C89: */
f(int i)
{ int ar[] = { 1 , 2 }; }

/* But this is not
   (because i is not a constant expression):
*/
f(int i)
{ int ar[] = { i , i+1 }; }

Это связано с 3.5.7 в C89 (цитата из проекта я нашел здесь):

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

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

В более поздних версиях C-стандарта не было этого ограничения, и стандартизированные версии С++ (начиная с С++ 98), я считаю, никогда не имели таких ограничений.

Я могу только догадываться, но, возможно, это то, что автор имел в виду?

Ответ 2

Я предполагаю, что автор предупреждает вас об отсутствии ограничений по размеру в C и С++. В C и С++ массивы распадаются до указателей на их первый элемент. Затем он использует арифметику указателя, чтобы найти элемент, на который ссылается индекс. Поскольку массивы не являются объектами, а компилятор не прилагает никаких усилий для хранения их размера, проверки длины не выполняются. В java массивы являются объектами, и поэтому их размер известен. Этот размер может быть проверен против, что безопасно защищает разработчика от доступа к памяти, которая не принадлежит ему/ей, когда перешагивает границы массива.

Мне кажется странным, что выражение "С++ использует агрегатную инициализацию, чтобы сделать его намного безопаснее" даже использовалось в этом контексте.

Агрегатная инициализация, которая является общей для большинства современных языков, выглядит следующим образом

int intArray[3] = {1,2,3};
int int2DArray[2][2] = {{1,2}, {3,4}};

Этот тип инициализации предполагает, что вы знаете размер массива заранее и его содержимое. Этот тип инициализации безопасен для защиты от переступания границы и обеспечивает инициализацию массива с установленными значениями. Возможно, в этом случае автор имеет в виду разработчика, который объявил статический массив C размером 5. Затем этот разработчик создает цикл для инициализации его содержимого, но переступает границу массива на единицу, записывая в память, которая не является его/ее.