
- •Федеральное агентство по образованию
- •Краткая история языка с
- •Особенности и основные понятия языка Си
- •Структура программы
- •Типы данных
- •Классификация типов данных
- •Вещественные
- •Логический
- •Диапазоны представления данных
- •Объявление переменных
- •Константы в языке Си
- •Операции языка Си. Приоритет операций
- •Операция присваивания
- •Арифметические операции
- •Операции отношения
- •Логические операции
- •Поразрядные операции
- •Операцияsizeof
- •Операция выбора по условию
- •Операция запятая
- •Приведение типов Неявное преобразование типов
- •Явное преобразование типов
- •Основные средства ввода-вывода на Си Понятие потока
- •Стандартные потоки
- •Функции потокового ввода-вывода
- •Функции ввода
- •Ввод символов
- •Ввод строк
- •Форматированный ввод
- •Операторы языка Си
- •Оператор if
- •Оператор switch
- •Оператор while
- •Оператор do while
- •Оператор for
- •Оператор break
- •Оператор continue
- •Функции вывода на экран
- •Вывод символов
- •Функция вывода строк puts()
- •Форматированный вывод
- •Указатели в языке Си
- •Адресная арифметика
- •Массивы
- •Функции в языке с
- •Прототипы функций
- •Указатели типа void
- •Функции, возвращающие указатели
- •Встраиваемые функции
- •Рекурсивные функции
- •Функции с переменным числом аргументов
- •Аргументы функции main()
- •Область действия и область видимости
- •Локальные переменные
- •Локальные переменные в функции Main()
- •Глобальные переменные
- •Классы памяти
- •Указатели на указатели
- •Указатели и многомерные массивы
- •Массивы указателей
- •Указатели на функции
- •Операции над строками символов Определение длины строки
- •Копирование строк
- •Поиск в строках
- •Преобразования символов в строках
- •Другие функции для работы со строками
- •Преобразования строк в числа
- •Преобразования чисел в строки
- •Функции анализа символов
- •Распределение памяти
- •Манипулирование блоками памяти
- •Типы, определяемые пользователем
- •Перечислимый тип
- •Структуры
- •Сложные структуры
- •Битовые поля в структурах
- •Объединения
- •Работа с файлами Связь между потоками и файлами
- •Типы дисковых файлов
- •Открытие файла
- •Запись и чтение данных
- •Форматированный ввод-вывод Форматированный вывод
- •Int fprintf (file *fp, char *fmt, ...);
- •Форматированный ввод
- •Символьный ввод
- •Символьный вывод
- •Блочный ввод-вывод
- •Закрытие файла
- •Директивы препроцессора
- •Директива #include
- •Директива #define
- •Директивы условной компиляции
- •Директива #undef
- •Предопределенные макросы
- •69 Лекции по курсу «Языки программирования» Часть III
Функции, возвращающие указатели
В прототипе и заголовке такой функции должен присутствовать знак ссылки по указателю (*). Общая форма объявления подобных функций:
тип *func(список_параметров);
В этом операторе объявляется функция func(), которая возвращает указатель на переменную заданного типа.
Пример такой функции: double *funcl(список_параметров);
Здесь объявляется функция, возвращающая указатель типа double.
Ни в коем случае не путайте функцию, которая возвращает указатель, с указателем на функцию. Если добавить в любое из этих объявлений лишнюю пару круглых скобок, получим указатель на функцию:
double (*func)(...); /* Указатель на функцию, возвращающую double */
double *func(...); /* Функция, возвращающая указатель на double */
Указатели не являются ни типом целое (int), ни типом беззнаковое целое (unsigned int). Их значениями являются адреса памяти данных определенного типа.
Встраиваемые функции
Функции этого типа появились в стандарте С-99.
Сам термин "встраиваемые" возник оттого, что вместо вызова функции выполняется ее прямая вставка (встраивание) в тело вызывающей функции.
Обычно встраиваемые функции невелики по размеру. Компилятор пытается выполнить их самым быстрым возможным способом.
Функцию можно сделать встраиваемой, добавив к ее объявлению ключевое слово inline.
Пример.
inline int sum( int a, ini b )
{
return (a + b);
}
Всюду, где используется функция sum(), компилятор постарается максимально оптимизировать ее выполнение. Вызов встраиваемых функций ничем не отличается от вызова любых других функций.
Рекурсивные функции
В языке С функции могут вызывать сами себя. Функция называется рекурсивной, если оператор в теле функции содержит вызов этой же функции. Классический пример рекурсивной функции - это вычисление факториала числа N! = 1*2*3*...*N.
Назовем эту функцию factorial().
factorial(int n)
{
int a;
if (n==l) return 1;
a = factorial(n-l)*n;
return a;
}
Вызов функции в рекурсивной функции не создает новую копию функции, а создает в памяти новые копии локальных переменных и параметров. Из рекурсивной функции надо предусмотреть выход, иначе это вызовет "зависание" системы.
Функции с переменным числом аргументов
Нам уже встречались библиотечные функции, принимающие переменное количество аргументов например, printf() или scanf().
При объявлении функции с переменным числом параметров сначала нужно перечислить фиксированные параметры — те, которые всегда присутствуют в вызове функции. Ее прототип должен содержать, по меньшей мере, один фиксированный аргумент. В конце списка параметров ставится многоточие (...) для того, чтобы указать, что следом должны идти дополнительные аргументы — один или несколько. При этом запятая после последнего фиксированного параметра перед многоточием необязательна, например:
void f(int a, int b, ...);
или
void f(int a, int b...);
Основная проблема функции с переменным числом параметров заключается в том, что ни тип передаваемых параметров, ни их количество неизвестны вызываемой функции. Существуют следующие подходы:
Как в функции printf() строка формата полностью определяет тип и число параметров через спецификации формата.
Можно зарезервировать какое-либо значение как завершающее.
Можно передавать число параметров в качестве фиксированного параметра.
Для создания функции, принимающей переменное количество аргументов разных типов, необходимо придумать способ передачи в нее информации об этих типах. Например, можно воспользоваться символьным кодом.
Средства для работы с переменным списком аргументов определены в заголовочном файле stdarg.h. Они используются внутри функции для получения аргументов из списка. Эти средства включают в себя:
va_list |
Тип данных для указателя на аргументы. |
va_start(list, last_fixed) |
Макрос для инициализации списка аргументов. |
va_arg(list, arg_type) |
Макрос для получения аргументов по очереди из списка. |
va_end(list) |
Макрос для очистки лишних данных после получения всех аргументов. |
где list – переменная типа va_list;
last_fixed – последний фиксированный параметр;
arg_type – тип аргумента.
Как работает этот механизм. Передаваемые функции параметры располагаются в памяти непосредственно друг за другом. Тип va_list – это указатель на void. Макрос va_start устанавливает этот указатель за последним фиксированным параметром, а это можно сделать, так как его адрес и тип известны. Макрос va_arg, используя приведение к типу, указанному в качестве второго параметра, возвращает значение, на которое указывает первый параметр, и передвигает указатель на размер переменной данного типа. Эта реализация накладывает некоторые ограничения. В Borland C++ надо аккуратно использовать нефиксированные параметры char, unsigned char, так как они занимают в памяти 1 байт, а в стеке под них отводится 2 байта.