Что представляет собой агрегатная инициализация
В разделе "Инициализация массива" в главе 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. Затем этот разработчик создает цикл для инициализации его содержимого, но переступает границу массива на единицу, записывая в память, которая не является его/ее.