|
Начало Программирование под UNIX Язык Си
Чем отличаются простая переменная и массив из одного элемента? Рассмотрим такие объявления:
int i;
int a[1];
Хотя, на первый взгляд, объекты a и i отличаются, почти все их аспекты в языке Си эквивалентны.
Так, у них один и тот же размер (sizeof); i и a[0] -- это значения типа int, причем lvalue
(то есть, они могут встречаться в левой части оператора присваивания); наконец, &i и a -- это константные
указатели на объект int.
Тем не менее, в ANSI Си между свойствами этих объектов есть важное отличие в области арифметики
указателей. Как мы знаем, индексация массива в Си сводится к суммированию базового указателя массива
и индекса с последующей разадресацией: a[j] -- то же, что и *(a+j). Также все мы знаем, что обращение за
границы массива -- это серьезная ошибка. Однако в языке Си ошибкой является не только операция *(a+j),
но и a+j, если ее результат выходит за границы массива. Точнее, результат этой операции неопределен, что
может означать любые неприятости, вплоть до упразднения языка Си и принудительного перехода на язык
Паскаль. На практике эту ситуацию можно представить так: если массив находится близко к концу адресного
пространства, вычисление указателя a+j при достаточно большом j может вызвать переполнение соответствующего
аппаратного регистра и сбой программы.
Тем не менее, программисты на Си давно используют такую идиоматическую конструкцию:
int tab[100], *p;
for (p = tab; p < tab + sizeof(tab); p++) {
...
}
На выходе из такого цикла p примет значение tab+sizeof(tab) и укажет за последний элемент массива!
Чтобы не ломать сложившуюся
практику, авторы ANSI Си внесли поправку: указатель в пределах массива a[N] может принимать значения
от a до a+N. Конечно, разадресовать значение указателя a+N при этом нельзя. Если мы снова представим
себе платформу, где переполнение указателя приводит к сбою, то в ней последний элемент массива, a[N-1],
не может занимать последнюю ячейку памяти в адресном пространстве.
Вернемся к нашему основному вопросу про i и a[0]. Выходит, что, согласно ANSI Си, указатель a+1 -- действительный,
хотя и не отвечает доступной ячейке памяти. В то же время, указатель &i+1 будет недействительным и, возможно,
приведет к сбою программы, например, если переменная i займет последнюю ячейку адресного пространства в нашей
платформе.
Чтобы ликвидировать это расхождение, стандарт Си-99 явно говорит, что по своим арифметическим свойствам
указатель на простую переменную эквивалентен указателю на первый элемент массива из одного элемента того же
типа. В нашей платформе теперь и простая переменная не может занимать последнюю ячейку адресного пространства.
Как мы видим, в Си-99 простая переменная и массив из одного элемента стали полностью эквиваленты,
но только благодаря дополнительным утверждениям стандарта. Не перемудрили ли его авторы? На самом деле,
нет, потому что им приходится учитывать большое разнообразие платформ, на которых язык Си должен
работать предсказуемо и в соответствии со стандартом.
Создано: Yar Tikhiy Последнее обновление: Yar Tikhiy
|