Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Название:Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:0101
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015 краткое содержание
Язык программирования C. Лекции и упражнения (6-е изд.) 2015 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Определение этой функции ограничено; она будет работать только с массивами типа int, содержащими 10 элементов. Более гибкий подход предполагает передачу во втором аргументе размера массива:
int sumlint * ar, int n) // более общий подход
{
int i;
int total = 0;
for ( i = 0; i < n; i++) // используются n элементов
total += ar [i]; // ar [i] - то же самое, что и *(ar + i)
return total;
}
Здесь первый параметр сообщает функции, где находится массив и какой тип данных он содержит, а второй параметр уведомляет функцию о том, сколько элементов имеется в массиве.
Есть еще один момент, который необходимо отметить касательно параметров функции. В контексте прототипа или заголовка определения функции, и только в этом контексте, вместо int * ar можно подставить int ar[]:
int sum (int ar[] , int n);
Массивы и указатели 385
Форма int * ar всегда означает, что ar является типом указателя на int. Форма int ar[] также означает, что ar — тип указателя на int, но лишь тогда, когда он применяется для объявления формальных параметров. Вторая форма напоминает читателю кода о том, что ar не только указывает на int, но указывает на значение int, которое представляет собой элемент массива.
НА ЗАМЕТКУ! Объявление параметров массива
Поскольку имя массива — это адрес его первого элемента, фактический аргумент в виде имени массива требует, чтобы соответствующий формальный аргумент был указателем. В этом и только в этом контексте С интерпретирует int ar[] как int * ar, т.е. ar является типом указателя на int. Поскольку в прототипах разрешено опускать имя, все четыре приведенных ниже прототипа эквивалентны: int sumlint *ar, int n); int sum(int *, int); int sumlint ar[], int n); int sum(int[], int);
В определениях функций имена опускать нельзя, поэтому следующие две формы определения эквивалентны:
int sumlint *ar, int n)
{
// здесь находится код
}
int sum(int ar[], int n);
{
// здесь находится код
}
Вы должны иметь возможность использовать любой из четырех показанных выше прототипов с любым из двух приведенных определений.
В листинге 10.10 показана программа, в которой применяется функция sum(). Чтобы отразить интересный факт, касающийся аргументов типа массива, в ней также выводится размер исходного массива и размер параметра функции, представляющего массив. (Если ваш компилятор не поддерживает спецификатор %zd, для вывода значений функции sizeof используйте спецификатор %u или, возможно, %lu.)
Листинг 10.10. Программа sum_arr1.c
386 Глава 10
Вывод в нашей системе имеет следующий вид:
Размер ar составляет 8 байтов.
Общая сумма элементов массива marbles равна 190.
Объем памяти, отведенной под массив marbles, составляет 40 байтов.
Обратите внимание, что размер массива marbles равен 40 байтов. Это имеет смысл, т.к. массив marbles содержит 10 значений типа int, каждое из которых занимает 4 байта, что в сумме составляет 40 байт. Но размер ar равен всего 8 байтов. Причина в том, что ar — это не сам массив, а указатель на первый элемент marbles. В нашей системе для хранения адресов применяются 8 байтов, поэтому размером переменной типа указателя будет 8 байтов. (В других системах может использоваться другое количество байтов.) Короче говоря, в листинге 10.10 имя marbles — это массив, ar — указатель на первый элемент массива marbles, а связь между массивами и указателями в языке С позволяет применять форму записи массива вместе с указателем ar.
Использование параметров типа указателей
Функция, работающая с массивом, должна знать, где начинать и где заканчивать свое действие. В функции sum() используется параметр типа указателя для идентификации начала массива и целочисленный параметр, отражающий количество элементов массива, которые нужно обработать. (Параметр типа указателя также описывает тип данных в массиве.) Но это не единственный способ сообщения функции того, что она должна знать. Другой способ описания массива предусматривает передачу функции двух указателей, первый из которых отмечает, где массив начинается (как и раньше), а второй — где он заканчивается. Этот подход иллюстрируется в листинге 10.11. Здесь также задействован тот факт, что параметр типа указателя является переменной, так что вместо применения индекса для сообщения о том, к какому элементу массива обращаться, в функции можно изменять значение самого указателя, заставляя его по очереди указывать на каждый элемент массива.
Листинг 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], т.е. значения, хранящегося в этой ячейке, поэтому программа не должна пытаться получить доступ к ней.
Читать дальшеИнтервал:
Закладка: