
- •Федеральное агентство по образованию
- •Краткая история языка с
- •Особенности и основные понятия языка Си
- •Структура программы
- •Типы данных
- •Классификация типов данных
- •Вещественные
- •Логический
- •Диапазоны представления данных
- •Объявление переменных
- •Константы в языке Си
- •Операции языка Си. Приоритет операций
- •Операция присваивания
- •Арифметические операции
- •Операции отношения
- •Логические операции
- •Поразрядные операции
- •Операцияsizeof
- •Операция выбора по условию
- •Операция запятая
- •Приведение типов Неявное преобразование типов
- •Явное преобразование типов
- •Основные средства ввода-вывода на Си Понятие потока
- •Стандартные потоки
- •Функции потокового ввода-вывода
- •Функции ввода
- •Ввод символов
- •Ввод строк
- •Форматированный ввод
- •Операторы языка Си
- •Оператор if
- •Оператор switch
- •Оператор while
- •Оператор do while
- •Оператор for
- •Оператор break
- •Оператор continue
- •Функции вывода на экран
- •Вывод символов
- •Функция вывода строк puts()
- •Форматированный вывод
- •Указатели в языке Си
- •Адресная арифметика
- •Массивы
- •Функции в языке с
- •Прототипы функций
- •Указатели типа void
- •Функции, возвращающие указатели
- •Встраиваемые функции
- •Рекурсивные функции
- •Функции с переменным числом аргументов
- •Аргументы функции main()
- •Область действия и область видимости
- •Локальные переменные
- •Локальные переменные в функции Main()
- •Глобальные переменные
- •Классы памяти
- •Указатели на указатели
- •Указатели и многомерные массивы
- •Массивы указателей
- •Указатели на функции
- •Операции над строками символов Определение длины строки
- •Копирование строк
- •Поиск в строках
- •Преобразования символов в строках
- •Другие функции для работы со строками
- •Преобразования строк в числа
- •Преобразования чисел в строки
- •Функции анализа символов
- •Распределение памяти
- •Манипулирование блоками памяти
- •Типы, определяемые пользователем
- •Перечислимый тип
- •Структуры
- •Сложные структуры
- •Битовые поля в структурах
- •Объединения
- •Работа с файлами Связь между потоками и файлами
- •Типы дисковых файлов
- •Открытие файла
- •Запись и чтение данных
- •Форматированный ввод-вывод Форматированный вывод
- •Int fprintf (file *fp, char *fmt, ...);
- •Форматированный ввод
- •Символьный ввод
- •Символьный вывод
- •Блочный ввод-вывод
- •Закрытие файла
- •Директивы препроцессора
- •Директива #include
- •Директива #define
- •Директивы условной компиляции
- •Директива #undef
- •Предопределенные макросы
- •69 Лекции по курсу «Языки программирования» Часть III
Массивы указателей
Массив — это совокупность элементов данных одного типа под единым именем. Указатели тоже являются элементами данных определенных типов, поэтому их также можно организовывать в массивы. В,, некоторых ситуациях такие программные конструкции могут оказаться очень мощными и полезными.
Пожалуй, самым распространенным применением массивов указателей следует считать массивы символьных строк. Объявив и инициализировав массив указателей типа char, можно манипулировать сразу большим количеством строк с помощью этого массива. Каждый элемент массива указывает на отдельную строку, поэтому их перебор, например, в цикле дает доступ ко всем строкам по очереди.
Массивы указателей типа char
Следующий оператор объявляет массив из десяти указателей типа char:
char *message[10];
Каждый элемент массива message является указателем типа char. Объявление можно совместить с инициализацией и выделением памяти для строк:
char *message[10] = { "Один", "Два", "Три" };
Этот оператор выполняет следующие действия.
Размешает в памяти массив из десяти элементов с именем message; каждый элемент массива message является указателем типа char.
Выделяет пространство в памяти (неважно, где именно — это забота компилятора) и размещает в нем три заданные строки, завершая каждую нулевым символом.
Инициализирует указатель message[0] адресом первого символа строки "Один", message[1] — адресом первого символа "Два", message[ 2 ] — первого символа "Три".
Инициализация и использование массива указателей типа char:
int count;
char *message[3] = { "Один", "Два", "Три" };
for (count = 0; count < 3; count++)
printf("%s ", message[count]);
Передача массива указателей в функцию:
void print_strings(char *p[], int n);
i:
main()
{
char *message[3] = { "Один", "Два", "Три" };
print_strings(message, 3);
}
void print_strings(char *p[], int n)
{
int count;
for (count = 0; count < n; count++)
printf("%s ", p[count]);
}
Функция print_strings() принимает два аргумента. Один из них — это массив указателей типа char, а второй — количество элементов в массиве. Таким образом, функция print strings () может использоваться для вывода на экран строк из массива указателей.
Указатели на функции
Указатели на функции — это дополнительное гибкое средство вызова функций.
При выполнении программы код каждой ее функции располагается в памяти по определенному адресу. Указатель на функцию содержит начальный адрес функции — ее точку входа.
С помощью указателей программа может выбрать вызов одной из нескольких функций по какому-либо критерию или условию.
Объявление указателя на функцию
Общая форма этого объявления:
тип (*ptr to_func) (список_параметров);
Этот оператор объявляет переменную ptr_to_func указателем на функцию, возвращающую значение заданного типа и принимающую указанный список_параметров. Вот несколько конкретных примеров:
int (*func1)(int x); объявляется указатель funcl на функцию, принимающую один аргумент типа int и возвращающую значение того же типа.
void (*func2)(double y, double z); объявляется указатель func2 на функцию с двумя аргументами типа double и возвращаемым значением типа void.
char (*func3)(char *p[]);объявляется указатель func3 на функцию, которая принимает массив указателей типа char и возвращает значение типа char.
void (*func4)(); переменная func4 объявлена как указатель на функцию без аргументов и с пустым типом (void) возвращаемого значения.
Зачем нужны скобки вокруг имени указателя? Почему бы не записать, например, следующее:
int *funcl(int x);
Получим функцию, возвращающую указатель.
Инициализация и использование указателя функцию
Единственное, что должно строго соблюдаться при инициализации — это соответствие возвращаемого типа и списка параметров функции. Например, в следующем фрагменте объявляется и определяется функция, а также указатель на эту функцию:
float sq(float x); // Прототип функции.
float (*ptr)(float x); // Объявление указателя.
float sq(float x) // Определение функции.
{
return x*x;
}
Поскольку^функция sq() и указатель ptr имеют совпадающие списки параметров и возвращаемых значений, ptr можно инициализировать следующим образом:
ptr = sq;
Затем можно вызвать функцию с использованием указателя:
f = ptr(x);
sq() можно вызвать двумя способами: sq(6.6) и ptr(6.6).
Итак, имя функции без скобок является указателем на функцию. (Проведите аналогию с массивом, имя которого без скобок — тоже указатель.) Так зачем же нужно объявлять отдельный указатель на ту же самую функцию? Дело в том, что имя функции само по себе — указатель-константа, которую нельзя изменить. (Снова напрашивается параллель с массивами) В противоположность этому указатель является переменной, и ее значение можно изменять, как угодно. В частности, в разные моменты времени такая переменная может указывать на разные функции, если в этом возникнет необходимость.