Листинг 10.11. Программа sum arr2.c

Массивы и указатели 387

Указатель start начинает со ссылки на первый элемент marbles, поэтому выражение присваивания total + = *start добавляет к total значение первого элемента (20). Затем выражение start++ инкрементирует переменную start, в результате чего она указывает на следующий элемент в массиве. Поскольку start указывает на тип int, ее значение увеличивается на размер типа int.
Обратите внимание, что для завершения цикла суммирования в функции sump() используется метод, отличающийся от применяемого в sum(). В качестве второго аргумента функции sum() выступает количество элементов, и это значение используется как часть проверки конца цикла:
for ( i = 0; i < n; i + +)
Однако в sump() для проверки окончания цикла применяется второй указатель:
while (start < end)
Поскольку производится проверка на предмет неравенства, последним обработанным элементом массива будет элемент, находящийся непосредственно перед элементом, на который указывает end. Это означает, что end в действительности указывает на ячейку, расположенную после финального элемента массива. В языке С гарантируется, что при выделении пространства памяти под массив указатель на первую ячейку после конца массива будет допустимым. Благодаря этому, конструкция подобного рода является допустимой, т.к. последним значением, которое start получает в цикле, будет end. Обратите внимание, что использование такого указателя на место “за пределами конца массива”, делает вызов функции лаконичным:
answer = sump(marbles, marbles + SIZE);
Из-за того, что индексация начинается с 0, marbles + SIZE указывает на элемент, следующий за концом массива. Если бы end указывал на последний элемент, а не на следующий за концом массива, пришлось бы применять такой код:
answer = sump(marbles, marbles + SIZE - 1);
Подобный код не только внешне менее элегантен, его еще и труднее запомнить, поэтому с большей вероятностью можно допустить ошибку. Между прочим, хотя язык С гарантирует допустимость указателя marbles + SIZE, нет никаких гарантий в отношении marbles [SIZE], т.е. значения, хранящегося в этой ячейке, поэтому программа не должна пытаться получить доступ к ней.
Тело цикла можно ужать до одной строки:
total += *start++;
Унарные операции * и ++ имеют один и тот же приоритет, но ассоциацию справа налево. Это означает, что операция ++ применяется к start, а не к * start.
388 глава 10
Другими словами, инкрементируется указатель, а не значение, на'которое он указывает. Использование постфиксной формы (start++вместо++start) приведет к тому, что указатель не инкрементируется до тех пор, пока указываемое им значение не будет добавлено к total. Если бы в программе применялось выражение *++start, то сначала бы инкрементировался указатель, а затем использовалось бы значения, на которое он указывает. Однако если бы в программе было задействовано выражение (*start)++, то сначала использовалось бы значение start и затем инкрементировалось бы значение, а не указатель. Тогда указатель остался бы нацеленным на тот же самый элемент, но элемент содержал бы новое число. Хотя обычно применяется запись *start++, форма * (start++) более понятна. В листинге 10.12 демонстрируются особенности приоритетов.
Листинг 10.12. Программа order.с

Вот вывод, полученный в результате запуска программы:
*р1 = 100, *р2 = 100, *рЗ = 300
*р1++ = 100, *++р2 = 200, (*рЗ)++ = 300
*р1 = 200, *р2 = 200, *рЗ = 301
Единственной операцией, которая изменяет значение массива, является (*рЗ)++. Другие две операции приводят к тому, что p1 и р2 начинают указывать на следующий элемент массива.
Комментарии: указатели и массивы
Как вы уже видели, функции, которые обрабатывают массивы, в действительности используют указатели в качестве аргументов, но при написании функций обработки массивов вы должны сделать выбор между формой записи в виде массива и формой записи посредством указателей. Применение формы записи для массивов, как в листинге 10.10, делает более очевидным тот факт, что функция работает с массивами. Кроме того, такая форма записи более привычна для программистов, перешедших с других языков, таких как FORTRAN, Pascal, Modula-2 или BASIC. Другие программисты могут быть больше приучены к работе с указателями и посчитают более естественной форму записи с использованием указателей вроде показанной в листинге 10.11.
Читать дальше