Слайды лекций - 2014 (лектор - Белеванцев А. А.) (1107979), страница 5
Текст из файла (страница 5)
Пусть sizeof (int) == 4и пусть текущее значение int* p1 равно 2012.После операции p1++ значение p1 будет 2016 (а не 2013),после операции p1 - 3 – значение 2000.при увеличении (уменьшении) на целое число iуказатель будет перемещаться на i ячеексоответствующего типа в сторону увеличения(уменьшения) их адресов.3Преобразование типа указателяУказатель можно преобразовать к другому типу, но такоепреобразование типов обязательно должно быть явным.Условие: исходный указатель правильно выравнен дляцелевого типа.
Значение указателя сохраняется.Иногда такое преобразование типов может вызватьнепредсказуемое поведение программы.#include <stdio.h>int main (void){double x = 200.35, y;int *p;p = (int *)&x; /* &x ссылается на double,а p имеет тип *int */y = *p;/* будет ли y присвоенозначение 200.35? */printf ("значение x равно %f\n", x);printf ("значение y равно %f\n", y);return 0;}4Преобразование типа указателяТипичный вывод (GCC, Linux):значение x равно 200.350000значение y равно 858993459.000000В присваивании y = *p; загрузка *p считываеттолько первые четыре байта области памятис адресом &x (т.к.
sizeof (int) в данном случаеравен 4)В представлении 200.35 в формате числа doubleпервые четыре байта соответствуют целому числу858993459Таким образом, необходимо учитывать, что операции суказателями выполняются в соответствии с базовым типомуказателя.5Преобразование типа указателяРазрешено также преобразование целого в указатель инаоборот (поведение определяется реализацией).Однако пользоваться этим нужно очень осторожно.aux = (void *) -1;Допускается присваивание указателя типа void * указателюлюбого другого типа (и наоборот) без явного преобразованиятипа указателя. Это позволяет использовать указатель типаvoid *, когда тип объекта неизвестен.Использование типа void * в качестве параметрафункции позволяет передавать в функцию указатель наобъект любого типа.6Указатели и массивыУказатель на первый элемент массива можно создать,присвоив переменной типа “указатель на тип элемента массива”имя массива без индекса:int array[15];int *p, *q;p = array;q = &array[0];p и q указывают на начало массива array[15]Значение array изменить нельзя, а значение p – можно.array не является l-значением, а p – являетсяarray = p; array++ – писать нельзя (это ошибки)p = array; p++ – писать можно (и нужно)7Указатели и массивыИндексирование указателейint *p, a[10]; /* два способа присвоить 100 *//* 6-ому элементу массива a[10] */p = a;*(p + 5) = 100; /* адресная арифметика */p[5] = 100; /* индексирование указателя */Сравнение указателейЕсли p и q являются указателями на элементы одного итого же массива и p < q, то:q – p + 1 равно количеству элементов массиваот p до q включительно.Можно написать:if (p < q)printf ("p ссылается на меньший адрес, чем q");8Массивы указателейУказатели могут быть собраны в массив:int *mu[27]; /* это массив из 27 указателей на int */int (*um)[27]; /* это указатель на массив из 27 int */Примерstatic void error (int errno){static char *errmsg[] = {"переменная уже существует","нет такой переменной",<…>"нужно использовать переменную-указатель"};printf ("Ошибка: %s\n", errmsg[errno]);}Имя массива указателей – пример многоуровневого указателя.Массив errmsg можно представить как char **errmsg9ФункцииОбъявление функции:тип_возвр_значения имя_функции(тип параметр,тип параметр, …, тип параметр);int atoi (char s[]);void QuickSort (char *items, int count);Тип возвращаемого значения void означает, что функция невозвращает значения.Определение функции:объявление_функции {тело_функции}Областью действия функции является весь программныйфайл, в котором она объявлена, начиная со строки,содержащей ее объявление.Если в программном файле вызывается какая-либо функция,она обязательно должна быть объявлена в этомпрограммном файле до ее вызова.Директива препроцессора #include <имя_библиотеки.h>вставляет в программу объявления всех функций10соответствующей библиотекиВызов функцииЕсли функция f() возвращает значение типа тип, то вызовэтой функции может иметь вид:v = f();,где v – переменнаятипа тип.Если функция f(параметр) не возвращает значений, вызовэтой функции имеет вид:f(аргумент);В языке Си все аргументы передаются по значению(т.е.
передаются только значения аргументов, и эти значениякопируются в память функции).Если аргументом является указатель, его значением можетбыть адрес объекта вызывающей функции, что обеспечиваетвызываемой функции доступ к объекту.11Указатели и аргументы функцийИспользуя аргументы-указатели, функция может обращаться кобъектам вызвавшей ее функции.Использование указателей позволяет избежать копированиясложных структур данных: вместо этого передаются указателина эти структуры.Пример.
Функция void swap(int x, int y);меняет местами значения переменных x и y.Неправильный вариант:void swap (int x, int y){int tmp;tmp = x;x = y;y = tmp;Правильный вариант:void swap (int *px, int *py){int tmp;tmp = *px;*px = *py;*py = tmp;}}12Вызов функцииМассив всегда передается с помощью указателя на егопервый элемент.int asum1d (int a[], int n) {int s = 0;for (int i = 0; i < n; i++)s += a[i];return s;}Можно объявить массив a в списке параметров как const a[].Функции с переменным числом параметров:int scanf (const char *, ...);Всегда должен быть явно задан хотя бы один параметр.После многоточия не должно быть других явных параметров.Обработка переменных параметров – файл stdarg.h.13ФункцииПример:#include <ctype.h>int atoi (char *s){int n, sign;for (; isspace (*s); s++);sign = (*s == '-') ? -1 : 1;if (*s == '+' || *s == '-')s++;for (n = 0; isdigit (*s); s++)n = 10 * (*s - '0');return sign * n;}Стандартная библиотека ctype содержит такие функции, как14isspace() , isdigit() и др.Возврат из функцииВозврат из функции в точку вызвавшей ее функции, следующейза точкой вызова функции, осуществляется:либо при выполнении оператора return,либо после выполнения последнего оператора функции,если она не содержит оператора return.#include <string.h>#include <stdio.h>void print_str_reverse (char *s){register int i;for (i = strlen (s) – 1; i >= 0; i--)putchar (s[i]);}Если тип функции не void, то в ее теле должен быть хотя быодин оператор return с возвращаемым значениемЕсли у функции несколько операторов return, возвратосуществляется немедленно по тому из них, который будетвыполнен первым.15Результат выполнения функцииВсе функции, кроме тех, которые относятся к типу void,возвращают значение, которое определяется выражением воператоре return.Помимо вычисления возвращаемого значения, функция можетизменять значения переменных вызывающей функции (поуказателю), а также изменять значения глобальных переменных.Результаты вызова функции, не связанные непосредственно свычислением возвращаемых значений, составляют побочныйэффект функции.Выделяют следующие виды функций:(1) Функции, которые выполняют операции над своимиаргументами с единственной целью – вычислить возвращаемоезначение.(2) Функции, которые обрабатывают данные и возвращаютзначение, которое показывает, успешно ли была выполнена этаобработка.(3) Функции, возвращающие несколько значений (черезуказатели-аргументы и через возвращаемое значение).(4) Функции, не возвращающие значений.
Все такие функции16имеют тип void.Результат выполнения функцииВозвращаемым значением может быть указатель. Требуется,чтобы в объявлении такой функции тип возвращаемогоуказателя был объявлен точно: нельзя объявлятьвозвращаемый тип как int *, если функция возвращаетуказатель типа char *.Пример функции, возвращающей указатель (поиск первоговхождения символа c в строку s):char *match (char c, char *s){while (c != *s && *s)s++;return s;}17РекурсияВ языке Си функция может быть рекурсивной,т.е.
вызывать саму себя:fib (5)int fib (int n){if (n == 1 || n == 2)return 1;elsereturn fib (n – 2) + fib (n – 1);}fib (3)fib (1)fib (2)fib (4)fib (2)fib (3)fib (1)fib (2)18РекурсияРекурсивные функции часто неэффективны по сравнению с ихнерекурсивными вариантами:int fibn (int n) {int i, g, h, fb;if (n == 1 || n == 2)return 1;elsefor (i = 2, g = h = 1; i < n; i++) {fb = g + h;h = g;g = fb;}return fb;}Функция fib работает за экспоненциальное время илинейную память,функция fibn – за линейное время и константную память.19Хвостовая рекурсия*Хвостовая рекурсия (tail recursion) – рекурсивный вызовв самом конце функции.