Смекни!
smekni.com

Основы алгоритмического языка С++ (стр. 8 из 21)

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

Тип возврата

Функции могут возвращать величины любого типа за исключением массивов и функций. Для этого посредством спецификатора типа"type-specifier" в объявлении функции можно специфицировать любой тип: основной, структуру или совмещение. Идентификатор функции может быть модифицирован одной или несколькими звездочками (*), чтобы объявить возвращаемую величину типа указателя.

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

Примеры:

int add(int, int); /* Example 1 */

double calc(); /* Example 2 */

char *strfind(char *,...); /* Example 3 */

void draf(void); /* Example 4 */

double (*sum(double, double)) [3]; /* Example 5 */ int (*select(void)) (int) ; /* Example 6 */

char *p; /* Example 7 */

short *q;

int prt(void *);

В первом примере объявляется функция, поименованная add, которая требует два аргумента типа int и возвращает величину типа int.

Во втором примере объявляется функция, поименованная calc, которая возвращает величину типа double. Список типов аргументов не задан. В третьем примере объявляется функция, поименованная strfind, которая возвращает указатель на величину типа char. Функция требует, по крайней мере один аргументуказатель на величину типа char. Список типов аргументов заканчивается запятой с многоточием, обозначающим, что функция может потребовать большее число аргументов.

В четвертом примере объявляется функция с типом возврата void (нет возвращаемой величины). Список типов аргументов также void, означающий отсутствие аргументов для этой функции.

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

В шестом примере функция, поименованная select, объявлена без аргументов и возвращает указатель на функцию. Указатель возврата ссылается на функцию, требующую один аргумент типа int и возвращающую величину типа int.

В седьмом примере объявлена функция prt, которая требует аргумент- указатель любого типа, и которая возвращает величину типа int. Любой указатель p или q могли бы быть использованы как аргументы функции без выдачи при этом предупреждающего сообщения.

Классы памяти

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

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

Хотя Си определяет два типа классов памяти, но, тем не менее, имеется следующих четыре спецификатора классов памяти:

auto

register

static

extern

Объекты классов auto и register имеют локальное время жизни. Спецификаторы static и extern определяют объекты с глобальным временем жизни. Каждый из спецификаторов класса памяти имеет определенный смысл, который влияет на видимость функций и переменных в той же мере, как и сами классы памяти. Термин "видимость" относится к той части программы, в которой могут ссылаться друг на друга функции и переменные. Объекты с глобальным временем жизни существуют на протяжении выполнения исходной программы, но они могут быть видимы не во всех частях программы. Видимость и связанная с ней концепция времени жизни рассмотрена в разделе 3.5.

Месторасположение объявления переменной или функции внутри исходных файлов также влияют на класс памяти и видимость. Говорят, что объявления вне определения всех функций и переменных относятся к внешнему уровню, а объявления внутри определений функций относятся к внутреннему уровню.

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

Объявления переменной на внешнем уровне

Объявления переменной на внешнем уровне используют спецификаторы класса памяти static и extern или совсем опускают их. Спецификаторы класса памяти auto и register не допускаются на внешнем уровне.

Объявления переменных на внешнем уровне- это определения переменных или ссылки на определения, сделанные в другом месте.

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

-переменная на внешнем уровне может быть определена путем ее объявления со спецификатором класса памяти static. Такая переменная может быть явно инициализирована константным выражением. Если инициализатор отсутствует, то переменная автоматически инициализируется нулем во время компиляции. Таким образом, объявления static int k = 16; и static int k; оба рассматриваются как определения;

-переменная определяется, когда она явно инициализируется на внешнем уровне. Например, int j = 3; это определение переменной.

Так как переменная определяется на внешнем уровне, то она видима в пределах остатка исходного файла, от места, где она определена. Переменная не видима выше своего определения в том же самом исходном файле ни в других исходных файлах программы, если не объявлена ссылка, которая делает ее видимой.

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

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

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

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

Есть одно исключение из правил, описанных выше. Можно опустить из объявления переменной на внешнем уровне спецификатор класса памяти и инициализатор. Например, объявление int n; будет правильным внешним объявлением. Это объявление имеет два различных смысла в зависимости от контекста.

1. Если где-нибудь в программе будет определена на внешнем уровне переменная с тем же именем, то объявление является ссылкой на эту переменную, как если бы был использован спецификатор класса памяти extern в объявлении.

2. Если нет такого определения, то объявленной переменной распределяется память во время линкования и переменная инициализируется нулем. Если в программе появится более чем одно такое объявление, то память распределится для наибольшего размера из объявленных переменных. Например, если программа содержит два неинициализированных объявления переменной i на внешнем уровне int i; и char i; то память во время линкования распределится под переменную i типа int.

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

Пример:

/*****************************************************

SOURCE FILE ONE *****************************************************/

extern int i; /* reference to i

defined below */

main()

{

i++;

printf("%d\n", i); /* i equals 4 */

next();

}

int i = 3; /* definition of i */

next()

{

i++;

printf("%d\n", i); /* i equals 5 */

other();

}

/***************************************************** SOURCE FILE TWO

*****************************************************/

extern int i; /* reference to i in

first source file */

other()

{

i++;

printf("%d\n", i); /* i equals 6 */

}

Два исходных файла в совокупности содержат три внешних объявления i. Одно объявление содержит инициализацию- int i = 3; , где глобальная переменная i определена с начальным значением равным 3.

Самое первое объявление extern в первом файле делает глобальную переменную видимой выше ее определения в файле.

Без объявления extern функция main не смогла бы сослаться на глобальную переменную i. Объявление extern переменной i во втором исходном файле делает глобальную переменную видимой в этом исходном файле.

Все три функции выполняют одну и ту же задачу: они увеличивают i на 1 и печатают получившееся значение. (Предполагается, что функция printf определена где-то еще в программе.). Печатаются величины равные 4, 5 и 6.