Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кетков.doc
Скачиваний:
17
Добавлен:
27.09.2019
Размер:
2.22 Mб
Скачать

7.2. Операции над указателями

Так как значениями указателей являются адреса ячеек оперативной памяти, то указатели можно сравнивать. Очевидно, что сравнение на равенство или на неравенство более информативно, чем сведения о том, какой объект лежит "выше" или "ниже".

Основные операции, чаще всего применяемые к указателям – сложение указателя с целым числом или вычитание из указателя целого числа. Обе они широко применяются в том случае, когда указатель связан с массивом. По сути дела, эти операции эквивалентны аналогичным процедурам над индексами элементов массива. И точно так же как прибавление к индексу означает переход к следующему элементу массива, прибавление 1 к указателю означает увеличение его текущего значения на количество байт, соответствующих типу указателя (точнее, типу данных, на которые указатель обязан смотреть):

int q[6]={1,2,3,4,5,6};

int *p = &q{3];

cout << *p++ <<endl; //выводится 4, p=&q[4]

cout << (*p)++ <<endl; //выводится 6=5+1

cout << *(p++) <<endl; //p=&q[5], выводится 6

Если при обработке некоторого массива используются два указателя p1 и p2, продвигаемые навстречу друг другу, то их разность (p2-p1) определяет количество элементов массива расположенных между этим двумя адресами.

7.3. Ссылки

Ссылки представляют особый вид данных, напоминающих указатели. Будучи объявлены в функции, они должны быть связаны с адресами конкретных объектов и после этого изменять свои значения не могут. В дальнейшем в рамках этой функции они выступают как синонимы своих объектов – такое ощущение, что одному и тому же объекту присвоено несколько имен:

int x;

int &rx=x; //объявление и инициализация ссылки

Ссылка rx является эквивалентом идентификатору x, т.е. операторы x=5 и rx=5 абсолютно идентичны. В этом варианте особой пользы от ссылки rx довольно мало – ее имя длиннее основного имени переменной. Однако при программировании в среде Borland C++ Builder довольно часто приходится иметь дело с надоедающе длинными обозначениями свойств объектов, и тогда применение разумной ссылки сокращает время набора программы:

TColor old_pc,&pc=TForm1->Image1->Canvas->Pen->Color;

............

//запоминание цвета пера

old_pc=pc; //вместо old_pc=TForm1->Image1->Canvas->Pen->Color;

//смена цвета пера

pc=clRed; //вместо TForm1->Image1->Canvas->Pen->Color=clRed;

............

//восстановление цвета пера

pc=old_pc; //вместо TForm1->Image1->Canvas->Pen->Color=old_pc;

Однако главное преимущество ссылок проявляется при спецификации параметров функций. Если формальный параметр объявлен в заголовке функции как ссылка, то упрощается его использование в теле функции (в отличие от указателей к именам ссылок ничего добавлять не надо) и становится более естественным вызов функций (вместо формальных параметров-ссылок указываются имена переменных).

Раздел 8. Функции и их аргументы

Функции являются основными программными единицами в языках C, C++. Из них как из кирпичиков складывается программа. В отличие от алголоподобных языков в программах на C, C++ не допускается вложенность функций. Любая программа на C должна содержать главную функцию с именем main, с которой начинается выполнение программы. Вызов всех остальных функций прямо или косвенно инициируется главной функцией.

Аргументы функций – один из основных способов обмена информацией между частями программы. Конечно, для этих целей можно использовать и другие средства – глобальные переменные, внешнюю память (файлы на дисках). Но обилие глобальных переменных заставляет очень тщательно согласовывать имена общих переменных, усиливает зависимость программных единиц друг от друга, налагает ограничения на выбор имен локальных переменных. Кроме того, общедоступность глобальных переменных может привести к несогласованному их изменению разными функциями. А использование внешней памяти в ряде случаев приводит к резкому замедлению работы программы.

В системах программирования на IBM-совместимых компьютерах используются два основных механизма передачи параметров – через стек и через машинные регистры. Первый способ наиболее распространен, т.к. он не ограничивает объем передаваемой информации. В качестве стека используется определенный участок оперативной памяти с фиксированным, но управляемым диапазоном адресов (размер стека можно регулировать при настройке компилятора). Специальный регистр "следит" за очередным доступным участком стека. По адресу, хранящемуся в этом регистре, можно положить нужную порцию данных в стек и одновременно продвинуть содержимое регистра стека. Для этой цели система машинных команд предусматривает специальную операцию PUSH (от англ. – протолкнуть). Вторая машинная операция POP (от англ. pop up – выскочить наверх) позволяет извлечь из стека очередную порцию данных с одновременной коррекцией регистра стека. Системная программа, обслуживающая стек, следит за тем, чтобы стек не переполнился при записи и не оказался пустым при извлечении данных. Иногда механизм работы стека сравнивают с магазином огнестрельного оружия – в нем, пуля, попавшая последней в рожок автомата, стреляет первой. Этим же объясняется технология обслуживания стека LIFO (Last Input – First Output, т.е. последним вошел – первым вышел). Зарядка магазина имитирует запись в стек, а процедура стрельбы напоминает извлечение данных из стека.

Второй механизм позволяет ускорить процедуру передачи параметров, помещая их в специально выделенные регистры процессора. Однако таких регистров мало, поэтому этот механизм применяют в тех случаях, когда количество передаваемых параметров не превышает трех. В системе программирования BCB для регистровой передачи параметров существует специальная конструкция быстрого вызова (fastcall).

Несмотря на то, что главную функцию никто не вызывает, у нее тоже могут быть параметры, передаваемые ей операционной системой при запуске из командной строки. В большинстве случаев таких параметров два:

int main(int argc, char* argv[])

или

int main(int argc, char **argv)

Предположим, что наша программа с именем nameprog.exe была запущена из командной строки со следующими аргументами:

>nameprog par1 par2 par3

Тогда первый аргумент главной функции argc будет равен 4 (имя программы входит в список параметров командной строки). Второй аргумент функции main представляет собой строковый массив, элементами которого являются отдельные компоненты командной строки:

argv[0]

argv[1]

argv[2]

argv[3]

"nameprog"

"par1"

"par2"

"par3"

Параметры командной строки позволяют упростить запуск программы, работа которой зависит от информации, набираемой вручную с клавиатуры. Вспомните довольно старую, но надежную программу архивации данных:

>pkzip –a –r arch.zip qq1.doc qq2.doc

Функция может возвращать значение, – результат своей работы, или выполнять некоторое другое действие, не связанное с возвратом результата. Например, функция clrscr() осуществляет очистку экрана, но не возвращает никакого значения. Если функция возвращает значение, то в ее заголовке перед именем функции должен быть указан тип возвращаемого значения:

double mid(double x,double y)

В теле функции, возвращающей значение, обязан присутствовать оператор return (от англ. – возврат), содержащий результат работы функции – ее значение:

double mid(double x, double y)

{ return (x+y)/2.; }

Если функция не возвращает значение, то в ее заголовке перед именем функции должен быть указан тип void. В этом случае в теле функции может встретиться оператор return без параметра. Но оператор return может и отсутствовать – выход из функции произойдет при достижении последней фигурной скобки:

void print_v(int *a,int n)

{ int j;

printf("\n");

for(j=0; j<n; j++)

printf("%8d",a[j]);

printf("\n");

}

Если перед именем функции не указан ни один из стандартных типов и отсутствует спецификатор void, то считается, что функция возвращает значение типа int.