C. Бочков - Язык программирования Си для персонального компьютера
Инициализация
Переменной в объявлении может быть присвоено начальное значение посредством инициализатора. Записи инициализатора в объявлении предшествует знак равенства
=<инициализатор>
Можно инициализировать переменные любого типа. Функции не инициализируются. Объявления, которые используют спецификацию класса памяти extern, не могут содержать инициализатор.
Переменная, объявленная на внешнем уровне без спецификации класса памяти, может быть инициализирована не более одного раза в каком-либо из исходных файлов, составляющих программу. Если же она явно не инициализирована ни в одном из исходных файлов, то компоновщик инициализирует ее нулевым значением.
Переменная класса памяти static, объявленная как на внешнем, так и на внутреннем уровне, может быть инициализирована константным выражением не более одного раза в исходном файле. Если ее явная инициализация отсутствует, то компилятор языка Си инициализирует ее нулевым значением.
Инициализация переменных класса памяти auto и register выполняется каждый раз при входе в блок (за исключением входа в блок по оператору goto), в котором они объявлены. Если инициализатор опущен в объявлении переменной класса памяти auto или register, то ее начальное значение не определено. Инициализация переменных составных типов (массив, структура, объединение), имеющих класс памяти auto, запрещена в СП MSC, но допускается в СП ТС даже для переменных, объявленных с модификатором const. Переменные составного типа, имеющие класс памяти static, могут быть инициализированы на внутреннем уровне.
Инициализирующими значениями для переменных внешнего уровня, а также переменных класса памяти static внутреннего уровня должно быть константное выражение (см. раздел 4.2.9). Переменные классов памяти auto и register могут быть инициализированы не только константными выражениями, но и выражениями, содержащими переменные и вызовы функций.
Базовые типы и указатели
Синтаксис:
=<выражение>
Значение выражения присваивается переменной. При необходимости выполняются правила преобразования типов.
Примеры:
int х = 10, у = 20; /* пример 1 */
register int *рх = 0; /* пример 2 */
int с = (3*1024); /* пример 3 */
int *b = &x; /* пример 4 */
В первом примере переменная х инициализируется константным выражением 10, переменная у инициализируется константным выражением 20. Во втором примере указатель рх инициализирован нулевым значением. В третьем примере используется константное выражение для инициализации переменной с. В четвертом примере указатель b инициализируется адресом переменной х.
Составные типы
Элементы объектов составных типов инициализируются только константными выражениями.
Инициализация объектов составных типов имеет следующий синтаксис:
= {<список инициализаторов>}
Список инициализаторов представляет собой последовательность инициализаторов, разделенных запятыми. Список инициализаторов заключается в фигурные скобки. Каждый инициализатор в списке представляет собой либо константное выражение, либо, в свою очередь, список инициализаторов. Таким образом, заключенный в фигурные скобки список может появиться внутри другого списка инициализаторов. Эта конструкция используется для инициализации тех элементов объектов составных типов, которые сами имеют составной тип.
Значения константных выражений из каждого списка инициализаторов присваиваются элементам объекта составного типа в порядке их следования.
Для инициализации объединения список инициализаторов должен содержать единственное константное выражение. Значение этого константного выражения присваивается первому элементу объединения. В СП ТС не обязательно заключать это константное выражение в фигурные скобки.
Наличие списка инициализаторов в объявлении массива позволяет не указывать число элементов по его первой размерности. В этом случае количество элементов в списке инициализаторов и определяет число элементов по первой размерности массива. Тем самым определяется размер памяти, необходимой для хранения массива. Число элементов по остальным размерностям массива, кроме первой, указывать обязательно.
Если в списке инициализаторов меньше элементов, чем в объекте составного типа, то оставшиеся элементы объекта неявно инициализируются нулевыми значениями. Если же число инициализаторов больше, чем требуется, то выдается сообщение об ошибке. Эти правила применяются и к каждому вложенному списку инициализаторов.
Пример 1:
int р[4][3] =
{
{1, 1, 1},
{2, 2, 2},
{3, 3, 3,},
{4, 4, 4,},
};
В примере объявляется двумерный массив р, размером 4 строки на 3 столбца, элементы первой строки инициализируются единицами, второй строки — двойками и т. д. Обратите внимание на то, что списки инициализаторов двух последних строк содержат в конце запятую. За последним списком инициализаторов (4,4,4,) также стоит запятая. Эти дополнительные запятые не требуются, но допускаются. Требуются только те запятые, которые разделяют константные выражения и списки инициализаторов. Если список инициализаторов не имеет вложенной структуры, аналогичной структуре объекта составного типа, то элементы списка присваиваются элементам объекта в порядке следования. Поэтому вышеприведенная инициализация эквивалентна следующей:
int p[4][3] = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4};
При инициализации объектов составных типов нужно внимательно следить за правильностью расстановки фигурных скобок в списках инициализаторов. В следующем примере этот вопрос иллюстрируется более детально.
Пример 2.
struct {
int n1, n2, n3;
} nlist[2][3] = {
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}), /* строка 1 */
{{10,11,12}, {13,14,15}, {15,16,17}} /* строка 2 */
}
В примере переменная nlist объявляется как двумерный массив структур, состоящий из двух строк и трех столбцов. Каждая структура содержит три элемента. В строке 1 значения присваиваются первой строке массива nlist следующим образом:
1) Первая левая фигурная скобка строки 1 информирует компилятор языка Си о том, что начинается инициализация первой строки массива nlist (т. е. nlist[0]).
2) Вторая левая фигурная скобка означает, что начинается инициализация первого элемента первой строки массива (т. е. nlist[0][0]).
3) Первая правая фигурная скобка сообщает об окончании инициализации структуры nlist[0][0]. Следующая левая фигурная скобка сообщает о начале инициализации второго элемента первой строки nlist[0][1].
4) Процесс инициализации элементов подмассива nlist[0] продолжается до конца строки 1 и заканчивается по последней правой фигурной скобке.
Аналогично, в строке 2 присваиваются значения второй строке массива nlist, т. е. nlist[1].
Следует понимать, что фигурные скобки, охватывающие инициализаторы строки 1 и строки 2, необходимы. Следующая конструкция, в которой внешние фигурные скобки опущены, неверна.
struct {
int n1, n2, nЗ;
} nlist[2][3] = {
{1, 2, 3}, {4, 5, 6), {7, 8, 9),/* строка 1 */
{10,11,12), {13,14,15}, {16,17,18} /* строка 2 */
};
В этом примере по первой левой фигурной скобке в строке 1 начинается инициализация подмассива nlist[0], который является массивом из трех структур. Значения 1, 2, 3 назначаются трем элементам первой структуры (nlist[0][0]). Когда встретится правая фигурная скобка (после значения 3), инициализация подмассива nlist[0] закончится и две оставшиеся структуры — nlist[0][1] и nlist[0][2] — будут по умолчанию инициализированы нулевыми значениями. Аналогично, список {4,5,6} инициализирует первую структуру во второй строке nlist (т. е. nlist[1][0]), а оставшиеся две структуры — nlist[l][l] и nlist[1][2] — по умолчанию инициализируются нулевыми значениями. Когда компилятор языка Си обнаружит следующий список инициализаторов {7,8,9), он попытается инициализировать подмассив nlist[2]. Однако, поскольку nlist содержит только две строки и элемента nlist[2] в нем не существует, будет выдано сообщение об ошибке.
Пример 3.
union {
char m[2][3];
int i, j, k;
} y = {
{'1'},
{'4'}
};
В третьем примере инициализируется переменная у типа объединение. Первым элементом объединения является массив; он и будет инициализироваться. Список инициализаторов {'1'} задает значения для первой строки массива (m[0]). Поскольку в списке всего одно значение, то только первый элемент строки массива — m[0][0] —инициализируется символом '1', а оставшиеся два элемента в строке инициализируются по умолчанию нулевыми значениями (символом ' '). Аналогично, первый элемент второй строки массива m инициализируется значением '4', а остальные элементы инициализируются по умолчанию нулевыми значениями.