Лекции (1171139), страница 14
Текст из файла (страница 14)
Указатель как бы сдвигается на n элементов вправо, если считать,48что индексы элементов массива возрастают слева направо. Аналогично вычитание целогочисла n из указателя означает сдвиг указателя влево на n элементов. Пример:int *p, *q;int a[100];p = &(a[5]); // записываем в p адрес 5-го//элемента массива ap += 7;// p будет содержать адрес 12-го эл-таq = &(a[10]);--q;// q содержит адрес элемента a[9]Значение указателя при прибавлении к нему целого числа n увеличивается напроизведение n на количество байтов, занимаемое одним элементом того типа, на которыйссылается указатель.
В программировании это называют масштабированием. Разностьдвух указателей - это количество элементов данного типа, которое умещается междудвумя адресами. Результатом вычитания указателей является целое число. Физически оновычисляется как разность значений двух адресов, деленная на размер одного элементазаданного типа. Операции сложения указателя с целым числом и разности двух указателейвзаимно обратны:intintintp =q =n =q =*p, *q;a[100];n;&(a[5]);&(a[12]);q - p;p + n;intintp =q =r =*p, *q, *r;a[100];&(a[5]);&(a[12]);p + q; // Ошибка! Указатели нельзя складывать.// n == 7// q == &(a[12])Подчеркнем, что указатели нельзя складывать! В отличие от разности указателей,операция сложения указателей (т.е. сложения адресов памяти) абсолютно бессмысленна.ЗАДАНИЯРаботу с массивами организовать через указатели.1.Переворот одномерного целочисленного массива – перестановка его элементов в обратном порядке.Выделить в отдельную функцию процедуру инвертирования.2.Перестановка головы и хвоста массива без использования промежуточного массива.
Алгоритм этойпроцедуры заключается он в том, что надо последовательно выполнить 3 инвертирования – головы массива,хвоста массива и всего массива целиком. Инвертирование выделить в отдельную функцию.1 2 3 4 5 6 7 8 9 10Голова: 1 2 3 4 5 6Хвост: 7 8 9 10Результат: 7 8 9 10 1 2 3 4 5 63.Определить количество разных элементов в целочисленном массиве. Алгоритм, выполняющую основнуюзадачу выделить в отдельную функцию.4.
Написать программу перемножения квадратных матриц. Операцию перемножение вынести в отдельнуюфункцию.49Представление программы в виде функцийФункции разбивают большие вычислительные задачи на более мелкие и позволяютвоспользоваться тем, что уже сделано другими разработчиками, а не начинать созданиепрограммы каждый раз "с нуля". В выбранных должным образом функциях "упрятаны"несущественные для других частей программы детали их функционирования, что делаетпрограмму в целом более ясной и облегчает внесение в нее изменений.Язык проектировался так, чтобы функции были эффективными и простыми виспользовании.
Обычно программы на Си состоят из большого числа небольшихфункций, а не из немногих больших. Программу можно располагать в одном илинескольких исходных файлах. Эти файлы можно компилировать отдельно, а загружатьвместе, в том числе и с ранее откомпилированными библиотечными функциямПроиллюстрируем механизм определения функции на примере функции power(m,n), которая возводит целое m в целую положительную степень n. Так, power(2, 5) имеетзначение 32. На самом деле для практического применения эта функция малопригодна,так как оперирует лишь малыми целыми степенями, однако она вполне может послужитьиллюстрацией. Итак, мы имеем функцию power и главную функцию main, пользующуюсяее услугами, так что вся программа выглядит следующим образом:#include <stdio.h>int power(int m, int n);/* тест функции power */main(){int i;for (i = 0: i < 10, ++i)printf(“%d %d %d\n”, i, power(2,i), power(-3,i));return 0;}/* возводит base в n-ю степень, n >= 0 */int power(int base, int n){int i, p;p = 1;for (i = 1; i <= n; ++i)p = p * base;return p;}Определение любой функции имеет следующий вид:тип-результата имя-функции (список параметров, если он есть){объявленияинструкции}Определения функций могут располагаться в любом порядке в одном или внескольких исходных файлах, но любая функция должна быть целиком расположенав каком-то одном.
Если исходный текст программы распределен по несколькимфайлам, то, чтобы ее скомпилировать и загрузить, вам придется сказатьнесколько больше, чем при использовании одного файла. В следующей строке изфункции main к power обращаются дважды.printf(“%d %d %d\n”, i, power(2,i), power(-3,i));При каждом вызове функции power передаются два аргумента, и каждый разглавная программа main в ответ получает целое число, которое затем приводитсяк должному формату и печатается. Внутри выражения power(2, i) представляетсобой целое значение точно так же, как 2 или i.В первой строке определения power:int power(int base, int n);указываются типы параметров, имя функции и тип результата. Имена параметровлокальны внутри power, это значит, что они скрыты для любой другой функции, так что50остальные подпрограммы могут свободно пользоваться теми же именами для своих целей.Последнее утверждение справедливо также для переменных i и p: i в power и i в mainне имеют между собой ничего общего.Далее параметром мы будем называть переменную из списка параметров,заключенного в круглые скобки и заданного в определении функции, а аргументом значение, используемое при обращении к функции.
Иногда в том же смысле мы будемупотреблять термины формальный аргумент и фактический аргумент.Значение, вычисляемое функцией power, возвращается в main с помощьюинструкции return. За словом return может следовать любое выражение:return выражение;Функция не обязательно возвращает какое-нибудь значение. Инструкция return безвыражения только передает управление в ту программу, которая ее вызвала, не передаваяей никакого результирующего значения. То же самое происходит, если в процессевычислений мы выходим на конец функции, обозначенный в тексте последнейзакрывающей фигурной скобкой. Возможна ситуация, когда вызывающая функцияигнорирует возвращаемый ей результат.Поскольку main есть функция, как и любая другая она может вернутьрезультирующее значение тому, кто ее вызвал, - фактически в ту среду, из которой былазапущена программа.
Обычно возвращается нулевое значение, что говорит о нормальномзавершении выполнения. Ненулевое значение сигнализирует о необычном илиошибочном завершении.Объявлениеint power(int m, int n);стоящее непосредственно перед main, сообщает, что функция power ожидает двухаргументов типа int и возвращает результат типа int. Это объявление, называемоепрототипом функции, должно быть согласовано с определением и всеми вызовами power.Если определение функции или вызов не соответствует своему прототипу, это ошибка.Аргументы. Вызов по значениюВ Си все аргументы функции передаются “по значению”. Это следует понимать так, чтовызываемой функции посылаются значения ее аргументов во временных переменных, а не сами аргументы.Такой способ передачи аргументов несколько отличается от “вызова по ссылке” в Фортране и спецификацииvar при параметре в Паскале, которые позволяют подпрограмме иметь доступ к самим аргументам, а не к ихлокальным копиям.Главное отличие заключается в том, что в Си вызываемая функция не можетнепосредственно изменить переменную вызывающей функции: она может изменитьтолько ее частную, временную копию.
В качестве примера приведем еще одну версиюфункции power, в которой как раз использовано это свойство./* power: возводит base в n-ю степень; n >= 0*/int power(int base, int n){int p;for (p = 1; n > 0; --n)p = p * base;return p;}Параметр n выступает здесь в роли временной переменной, в которой циклом for вубывающем порядке ведется счет числа шагов до тех пор, пока ее значение не станетнулем. При этом отпадает надобность в дополнительной переменной i для счетчика цикла.Что бы мы ни делали с n внутри power, это не окажет никакого влияния на сам аргумент,копия которого была передана функции power при ее вызове.При желании можно сделать так, чтобы функция смогла изменить переменную ввызывающей программе.
Для этого последняя должна передать адрес подлежащейизменению переменной (указатель на переменную), а в вызываемой функции следует51объявить соответствующий параметр как указатель и организовать через него косвенныйдоступ к этой переменной.Механизм передачи массива в качестве аргумента несколько иной. Когдааргументом является имя массива, то функции передается значение, которое являетсяадресом начала этого массива; никакие элементы массива не копируются. С помощьюиндексирования относительно полученного значения функция имеет доступ к любомуэлементу массива.Внешние переменныеПрограмма на Си обычно оперирует с множеством внешних объектов: переменныхи функций. Прилагательное "внешний" (external) противоположно прилагательному"внутренний", которое относится к аргументам и переменным, определяемым внутрифункций.
Внешние переменные определяются вне функций и потенциально доступны длямногих функций. Сами функции всегда являются внешними объектами, поскольку в Сизапрещено определять функции внутри других функций. По умолчанию одинаковыевнешние имена, используемые в разных файлах, относятся к одному и тому же внешнемуобъекту (функции). (В стандарте это называется редактированием внешних связей(линкованием) (external linkage).)Поскольку внешние переменные доступны всюду, их можно использовать вкачестве связующих данных между функциями как альтернативу связей через аргументыи возвращаемые значения.
Для любой функции внешняя переменная доступна по ееимени, если это имя было должным образом объявлено.Если число переменных, совместно используемых функциями, велико, связи междупоследними через внешние переменные могут оказаться более удобными иэффективными, чем длинные списки аргументов.
Внешние переменные полезны, так какони имеют большую область действия и время жизни. Автоматические переменныесуществуют только внутри функции, они возникают в момент входа в функцию иисчезают при выходе из нее. Внешние переменные, напротив, существуют постоянно, такчто их значения сохраняются и между обращениями к функциям. Таким образом, еслидвум функциям приходится пользоваться одними и теми же данными и ни одна из них невызывает другую, то часто бывает удобно оформить эти общие данные в виде внешнихпеременных, а не передавать их в функцию и обратно через аргументы.Области видимостиФункции и внешние переменные, из которых состоит Си-программа, каждый разкомпилировать все вместе нет никакой необходимости. Исходный текст можно хранить внескольких файлах.