Приведение типов

Обзор

Указатели. Массивы. Строки

Механизм вызова функций, подставляемые функции

Рекурсия

Языки «С» и «С++» разрешают использование рекурсии, то есть вызова функцией самой себя.

Пример (функция вычисления факториала)

long fact(int k)

{

if (k<0) return 0;

if (k<=1) return 1;

return k*fact(k-1);

}

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

— сохранения контекста выполнения функции, из которой происходит вызов: заключается, прежде всего, в сохранении некоторых регистров процессора;

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

— вызов функции по её адресу.

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

Таким образом, вызов функции является достаточно ресурсозатратным процессом. Это в особенности справедливо, если тело функции не велико, а количество вызовов достаточно большое (например, вызов происходит в цикле). Казалось бы, в этом случае эффективнее не выделять в программе отдельные функции, но такое решение может не соответствовать принципам структурного программирования. Для такой ситуации введены подставляемые (inline) функции. Их тело компилируется непосредственно в точку их вызова, а не в отдельную область памяти. На такие функции накладывается ряд достаточно очевидных ограничений: в них недопустима рекурсия, нельзя объявить указатель на такую функцию. Большинство современных компиляторов автоматически выбирает режим (стандартный / подстановка) для функций.

inline int f(); //пример объявления подставляемой функции

Переменные связаны с адресом их размещения, но единственной доступной операцией управления этим адресом является получение его значения (операция «&»).

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

— реализация динамических (меняющих свою структуру во время выполнения программы) структур данных — такие структуры рассматриваются в третьей главе;

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

— доступ к сервисам операционной системы или оборудования, доступным по заданному адресу.

В этом случае используют указатель. Указатель на переменную хранит адрес её размещения. Обратиться к переменной можно через операцию разыменовывания указателя (*). Объявление указателя осуществляется символом «*» перед именем переменной. Пример:

int x;

int *t; // объявили указатель t на переменную типа int

t = &x; // присваиваем указателю адрес переменной x

*t = 4; // используя указатель, присваиваем x значение 4

Указатели и объекты одного типа могут быть объявлены вместе. Пример (другая реализация предыдущего примера):

int x,*t=&x;

Константа NULL предназначена для обозначения неинициализированных указателей.

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

Для обращения к полям структур, передаваемых по указателю, используется символ «->».

Пример:

struct S {int a; float b;};

int Func(S*s)

{

(*s).a = 3; // можно обратиться к полю так

s->b = 5.6 // а можно и так

return 0;

}

В языке «C» простые типы данных автоматически приводятся к требуемому. Вещественные преобразуются к целым путём отсечения дробной части. Данные большего размера приводятся к данным меньшего размера отбрасыванием старших байт. Такое приведение называется неявным.

Для производных и структурированных типов данных, в том числе для указателей, требуется явное преобразование типов. Операция преобразования типа имеет вид:

(новый_тип)переменная

Такое приведение называют явным.

При этом для объектов (экземпляров классов и структур) операция приведения должна быть определена. Для указателей допустимо приведение к другому указателю. Приведение указателей никак не преобразует сами данные. Указатель любого типа может быть приведен к нетипизированному указателю неявно.

Примеры приведены далее.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: