Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Программирование на языке высокого уровня Лекции 09.03.01

.pdf
Скачиваний:
29
Добавлен:
10.01.2021
Размер:
1.1 Mб
Скачать

2.int a[]={3,7,11,8,2}, i, max; max=*a;

for(i=1;i<=4;i++)

if(*(a+i)>=max)

max=*(a+i);

3.int a[]={3,7,11,8,2}, i, *t; int max=*(t=a); for(i=1;i<=4;i++)

if(*(t+i)>=max)

max=*(t+i);

4.int a[]={3,7,11,8,2}, *t; int max=*a; for(t=a;t<=a+4;t++)

if(*t>=max)

max=*t;

При работе с символьными массивами с использованием указателей можно вообще явным образом не выделять память под массив:

char a1,a2;

a1=”Кругом одни враги”; a2=a1+17;

while (--a2>=a1) printf(“%c”,*a2);

printf(“\n”);

Темы 21-22. Классы памяти. Функции. Способы организации взаимодействия между функциями.

Аргументы и возвращаемые значения функций.

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

Чтобы вызвать функцию необходимо обратиться к ней по имени в качестве оператора или операнда, указав после имени в круглых скобках список аргументов, которые необходимо передать. Имена переменных в этом списке пишутся через запятую без указания типа. Такие аргументы называются фактическими параметрами.

В результате вызова управление будет передано в вызванную функцию, а затем вернётся к вызывающей функции, когда вызванная функция выполнит все свои инструкции или когда встретится

ключевое слово return.

 

 

main()

r()

 

{ r();)

 

{putchar(‘r’);

Программирование на языке высокого уровня 09.03.01

21

a()

a();

{putchar(‘a’);

putchar(‘r’);

d();

}

putchar(‘a’);

 

}

 

d()

 

{putchar(‘d’);}

 

Заголовок любой функции имеет вид:

 

тип возвращаемого значения ИМЯ (список параметров)

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

Если функция возвращает значение, то её можно вызывать и в качестве оператора, и в качестве операнда. Но, если вызвать её в качестве оператора, то возвращаемое значение будет пропадать

Если функция ничего не возвращает, то её можно вызвать только в качестве оператора. У таких функций в заголовке в качестве типа возвращаемого значения указывается ключевое слово void.

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

формальных параметров в вызываемой функции.

 

 

void main()

 

 

 

 

{int a,b,t;

 

 

 

 

. . .

 

 

 

 

t=power(a,b);

 

 

 

 

. . .

 

 

 

 

}

 

 

 

 

 

int power(int base, int n)

 

 

 

{int a;

 

 

 

 

for(a=1;n>0;--n)

 

 

 

 

 

a=a*base;

 

 

 

 

return a; }

 

 

 

 

Никакие

действия,

которые

происходят

с

формальными

параметрами в вызываемой функции, не могут привести к изменению значений фактических параметров. Это связано с тем, что сами переменные не передаются в вызываемую функцию. Компилятор перед передачей управления делает копии фактических параметров, и именно эти копии попадают в вызываемую функцию. Такой способ передачи аргументов называется передачей по значению. При передаче по значению обратно в вызывающую функцию может

Программирование на языке высокого уровня 09.03.01

22

вернуться только значение переменной, упомянутой в операторе return. И тип именно этой переменной и является типом возвращаемого значения, который указывается в заголовке функции.

Если из одной функции в другую в качестве параметра передаётся массив, то механизм передачи другой. В функцию передаётся адрес нулевого элемента массива, и она фактически получает доступ ко всем элементам массива. Копирование элементов в этом случае не производится.

void main()

void funk(int t[100])

{int a[100];

{ . . .}

. . .

 

funk(a); . . .}

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

void main(void)

{int a,b;

. . .

top(&a,&b);

. . .

}

void top (int *w, int *e) { int c;

c=*w; *w= *e; *e=c;

}

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

тип возвращаемого значения ИМЯ(); или тип возвращаемого значения ИМЯ(список типов параметров);

Прототип можно не указывать, если заголовок функции

оказывается в программном коде раньше её

первого вызова. Если

всё-таки приходиться писать прототипы, то

их часто указывают

один раз перед

самой первой

функцией в файле. Однако для

документирования

программ и с

целью возможности использования

Программирование на языке высокого уровня 09.03.01

23

функций в разных проектах иногда советуют дополнительно описывать каждую функцию в каждой вызывающей функции.

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

void main(void)

Область действия переменных. Классы памяти.

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

void main(void) {int x=1;

{int x=7; printf(“%d\n”,x--);

}

printf(“%d\n”,++x);

}

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

являются локальными.

 

 

 

Регистровые переменные хранятся

не в

оперативной

памяти, а

в регистрах процессора, благодаря

чему

программа,

возможно,

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

Компилятор может не выделить регистр для такой переменной, но, независимо от того, выделялся на самом деле регистр или нет, для таких переменных не определено понятие адреса.

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

Программирование на языке высокого уровня 09.03.01

24

те значения, с которыми они остались после предыдущего вызова подпрограммы.

void main(void) {kirpich();

kirpich();

}

void kirpich(void)

{static int x; x+=3;

printf(“%d\n”;x);

}

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

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

По умолчанию внешние переменные получают начальные значения равные нулю.

Внешние переменные в обязательном порядке описываются вне

функций,

а кроме того должны

дополнительно

описываться

в

функции,

которая намеревается их

использовать,

если только

они

не описаны выше этой функции в том же самом программно файле. Для такого дополнительного описания внешних переменных внутри функций используется ключевое слово extern.

int x;

void main(void)

{printf(“x=%d\n”,x); gnus();

giena();

}

void gnus(void) {extern int y;

x=x+7; y=y-8;

printf(“x=%d\n”,x);

}

int y=11;

Программирование на языке высокого уровня 09.03.01

25

void giena(void) {x=x-3;

printf(“x=%d\n”,x);

}

Программирование на языке высокого уровня 09.03.01

26

Раздел 3. Типы, определяемые программистом. Лекции 17-19.

Тема 23. Структурные типы данных. Операции над переменными структурных типов.

Структурный тип данных - это тип, созданный на основе одного или нескольких базовых типов так, что каждая переменная структурного типа содержит под одним именем набор из нескольких переменных ранее определенных или базовых типов.

Описание структурного типа начинается с ключевого слова

struct и содержит список описаний, заключенный

в фигурные

скобки. За словом struct следует имя структурного типа.

struct point {

 

int x;

 

int y;

 

};

 

Имя структурного типа может служить кратким

обозначением

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

Аналогично

синтаксису

обычных операторов описания, за

правой фигурной

скобкой

могут следовать имена переменных,

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

struct point {...} x,y,z;

 

int x, y, z;

 

Описание

структурного

типа,

не

содержащее

списка

переменных, не резервирует памяти: оно просто описывает шаблон или образец структурной переменной. Однако, если структурный тип имеет имя, то этим именем можно пользоваться при описании структурных объектов:

struct point pt;

Структурная переменная при ее определении может быть

инициализирована при

помощи списка

инициализаторов, состоящего

из константных выражений:

 

 

 

struct point ttt = {300,400}.

 

 

Инициализировать

структурные

переменные

можно

также

присваиванием или обращением к функции, возвращающей результат в виде переменной соответствующего структурного типа.

Программирование на языке высокого уровня 09.03.01

27

Доступ к отдельному компоненту структурной переменной осуществляется посредством конструкции имя_переменной.компонент.

printf("%d,%d",pt.x,pt.y);

Как указывалось выше,среди компонентов при создании нового структурного типа могут быть и переменные ранее созданных структурных типов:

struct rect{

struct point pt1; struct point pt2;

};

struct rect screen;

Доступ к координате х точки pt1 - screen.pt1.x

Над структурными переменными возможны присваивание, взятие адреса с помощью операции & и осуществление доступа к ее компонентам. Сравнивать структурные переменные нельзя. Частным случаем этих операций является передача структурных переменных функциям и возврат их из функций в виде результата. В функцию можно передавать компоненты структурных переменных по отдельности, всю структурную переменную целиком, а также указатель на структурную переменную.

Пример возврата структурной переменной целиком: main()

{struct rect screen; struct point middle;

struct point makepoint(int,int); screen.pt1=makepoint(0,0); screen.pt2=makepoint(XMAX,YMAX); middle=makepoint((screen.pt1.x+ screen.pt2.x)/2,

(screen.pt1.y+screen.pt2.y)/2);

}

struct point makepoint (int x, int y) {struct point tmp;

tmp.x=x;

tmp.y=y; return tmp; }.

Пример функции, оба аргумента которой и возвращаемое значение – структурные переменные:

struсt point addpoint (struct point p1, struct point

p2)

{p1.x+=p2.x;

p1.y+=p2.y; return p1;

}

Программирование на языке высокого уровня 09.03.01

28

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

struct point *pp;

(*pp).x и (*pp).y - члены стуктуры.

Наличие скобок существенно из-за того, что приоритет опрератора . выше приоритета оператора *. Запись без скобок

*pp.x будет вообще неверной,

так как pp.x не является

указателем.

Если

используется

указатель

на

структурную

переменную,

то обычно пользуются

иной формой

доступа к ее

членам:

pp->x, pp->y.

Операторы . и -> выполняются слева направо, то есть при наличии описания

struct rect r, *rp=r;

можно обойтись без скобок в выражениях (r.pt1).x и (rp->pt1).x. Операторы доступа к компонентам структурных переменных

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

struct {

int len; char *str;

} *p;

оператор

++p->len;

увеличит на 1 значение компонента len, а не указателя р.

Тема 24. Массивы переменных структурных типов.

При описании

struct key { char *word; int count;

} massiv [100];

будет описан структурный тип key и выделена память под массив с именем massiv, каждый элемент которого является переменной этого типа.

Для обращения к элементам массивов структурных перемнных используются те же механизмы, что и при обращении к обычным структурным переменным:

Программирование на языке высокого уровня 09.03.01

29

for (i=0;i<100;i++) massiv[i].count++;

С массивами структурных перемнных можно работать и используя указатели:

struct key *p;

for (p=keytab;p<keytab+99;p++) if(p->count>0) printf("%4d%s\n",p->count,p->word);

Используя структурные перемнные и, в особенности, массивы структурных переменных, средствами языка С/С++ можно решать ряд задач обработки таблиц, то есть выполнять часть функций систем управления базами данных.

Тема 25. Объединения. Поля битов.

Объединение - это переменная, которая может в разные моменты времени содержать в себе объекты различных типов и размеров. Размер памяти, выделяемый под объединение, таков, чтобы хватило места под любой член объединения. Синтаксис доступа к членам объединения в точности такой же, как и при работе со структурными переменными.

union room { int giena;

char poleno; float tryp;

}k535;

Вслучае рассогласования типов результат не предсказуем и зависит от реализации (то есть от типа компилятора). За соблюдением этого требования должен следить сам программист, так как рассогласование типов не является синтаксической ошибкой.

Объединения могут являться компонентами структурных переменных и их массивов и, наоборот, объединения могут включать эти компоненты в свой состав. Доступ к членам объединений, входящих в структурные переменные, такой же, как у вложенных структурных переменных. Например, в массиве структурных переменных

struct {

char *name; int flags; int utype; union {

int ival; float fval; char *sval; } u;

Программирование на языке высокого уровня 09.03.01

30