
- •Содержание
- •Лекция 1. Общее знакомство
- •Происхождение языка Си
- •Достоинства языка Си
- •Будущее языка Си
- •Использование языка Си
- •Использование текстового редактора для подготовки программ
- •Исходные и выполняемые файлы
- •Пример простой программы на языке Си
- •Пояснения к программе
- •Первый просмотр
- •Второй просмотр
- •Структура простой программы
- •Дополнительный пример
- •Лекция 2. Данные, символьные строки, директива #define
- •Основные типы данных
- •Описание различных типов, переменные и константы
- •Символьные строки
- •Препроцессор языка Си
- •Лекция 3. Операции
- •Основные операции
- •Операция вычитания: -
- •Операция изменения знака: -
- •Операция умножения: *
- •Операция деления: /
- •Дополнительные операции
- •Операция деления по модулю: %
- •Операция уменьшения: --
- •Перечень операций языка Си
- •Операции, уровень приоритета которых равен 1
- •Операция уменьшения: --
- •Операция вычитания: -
- •Операции, уровень приоритета которых равен 5
- •Операции, уровень приоритета которых равен 6
- •Операции, уровень приоритета которых равен 7
- •Операция, уровень приоритета которой равен 8
- •Операция, уровень приоритета которой равен 12
- •Операция логическое или: ||
- •Операция, уровень приоритета которой равен 13
- •Операция условный оператор: ?
- •Операция, уровень приоритета которой равен 14
- •Операция присваивания
- •Операция, уровень приоритета которой равен 15
- •Операция запятая: ,
- •Лекция 4. Операторы
- •Выражения
- •Простейшие выражения
- •Операторы
- •Составные операторы
- •Оператор цикла while
- •Изучение и использование функций printf( ) и scanf( )
- •Применение функции scanf( )
- •Лекция 5. Преобразование типов
- •Эквивалентность типов
- •Преобразование типов
- •Разбор программы
- •Операция приведения
- •Неявное преобразование типа
- •Арифметические преобразования
- •Явные преобразования типов
- •Синтаксис типов
- •Лекция 6. Функции и переключение ввода-вывода
- •Ввод и вывод одного символа
- •Чтение одной строки
- •Чтение файла
- •Переключение и работа с файлами
- •Переключение ввода
- •Комбинированное переключение
- •Операционные системы, отличные от oc unix
- •Лекция 7. Выбор вариантов
- •Выбор вариантов
- •Оператор if
- •Расширение оператора if
- •Операции отношения
- •Что такое истина
- •Осложнение с понятием истина
- •Логические операции
- •Операция условия: ?:
- •Множественный выбор
- •Лекция 8. Циклы и другие управляющие средства. Структурное программирование
- •Структурное программирование
- •Цикл с предусловием
- •Цикл со счетчиком
- •Цикл с постусловием
- •Другие управляющие операторы
- •Оператор break
- •Оператор continue
- •Оператор goto
- •Лекция 9. Функции
- •Создание и использование функций
- •Аргументы функции
- •Возвращение значений
- •Локальные переменные
- •Нахождение адресов
- •Указатели, первое знакомство
- •Операция косвенной адресации *
- •Описание указателей
- •Подведем итоги по указателям
- •Функции с переменным количеством аргументов
- •Лекция 10. Классы памяти и разработка программ
- •Классы памяти и область действия
- •Автоматические переменные
- •Внешние переменные
- •Статические переменные
- •Внешние статические переменные
- •Регистровые переменные
- •Лекция 11. Препроцессор языка Си
- •Общие сведения
- •Символические константы: #define
- •Замена идентификаторов
- •Использование аргументов с #define
- •Макроопределение или функция?
- •Включение файла: #include
- •Условная компиляция
- •Вспомогательные директивы Номер строки и имя файла
- •Реакция на ошибки
- •Пустая директива
- •Встроенные макроимена
- •Лекция 12. Массивы и указатели
- •Указатели и массивы
- •Массивы
- •Указатели
- •Динамические объекты
- •Создание динамических объектов
- •Доступ к динамическим объектам
- •Время жизни динамического объекта
- •Связь между указателями и массивами
- •Строки - дополнительные сведения о тесной связи между указателями и массивами
- •Инициализация массивов и классы памяти
- •Функции, массивы и указатели
- •Операции с указателями
- •Лекция 13. Символьные строки и функции над ними
- •Строковые константы
- •Массивы символьных строк и их инициализация
- •Массив и указатель: различия
- •Указатели и строки
- •Ввод-вывод строк
- •Обработка строк
- •Лекция 14. Структуры
- •Определение структурных переменных
- •Доступ к компонентам структуры
- •Поля битов в структурах
- •Объединения
- •Перечисления
- •Переменные структуры
- •Указатели и структуры
- •Массив структур
- •Переименование типов
- •Лекция 15. Библиотека языка Си и файлы ввода-вывода
- •Стандартные библиотечные функции
- •Доступ в библиотеку языка Си
- •Открытие файла: fopen( )
- •Закрытие файла: fclose( )
- •Текстовые файлы с буферизацией
- •Ввод-вывод текстового файла: getc( ), putc( )
- •Ввод-вывод файла: fprintf( ), fscanf( ), fgets( ), fputs( )
- •Функции fprintf( ) и fscanf( )
- •Функция fgets( )
- •Функция fputs( )
- •Функция fseek( )
- •Распределение памяти Функция malloc( )
- •Функция calloc( )
- •Лекция 16. Функции в примерах
- •Функция получения случайных чисел
- •Поиск узлов из простых чисел
- •Матрица инцидентности
- •Структуры данных
- •Очереди
- •Связанные списки
- •Все операции со стеком
- •Подведем итог
- •Дополнения
- •Литература
Аргументы функции
Формальный аргумент - переменная в вызываемой функции, а фактический аргумент - конкретное значение, присвоенное этой переменной вызывающей программой. Фактический аргумент может быть константой, переменной или более сложным выражением. Независимо от типа фактического аргумента он вначале вычисляется, а затем его величина передается функции. Фактический аргумент - это конкретное значение, которое присваивается переменной, называемой формальным аргументом.
Если для связи с некоторой функцией требуется более одного аргумента, то наряду с именем функции можно задать список аргументов, разделенных запятыми. Например:
void print_num(i,j)
int i,j;
{
printf("значение i=%d. Значение j=%d.", i,j);
}
Обращение в программе к данной функции будет таковым:
print_num(6,19);
Особое внимание нужно уделить правилам передачи аргументов при обращении к функциям. Синтаксис языка Си предусматривает только один способ передачи аргументов - передачу по значениям. Это означает, что формальные параметры (аргументы) функции локализованы в ней, то есть недоступны вне определения функции и никакие операции над формальными параметрами в теле функции не изменяют значения фактических параметров. Передача параметров по значению предусматривает следующие действия:
При компиляции функции выделяются участки памяти для формальных параметров. Формальные параметры оказываются внутренними объектами функции. При этом для параметров типа float формируются объекты типа double. Для параметров типа char, short int создаются объекты типа int. Если параметром является массив, то формируется указатель на начало этого массива и он служит представлением массива-параметра в теле функции (об указателе массивов описано в 12 лекции данного курса).
Вычисляются значения выражений, использованных в качестве фактических параметров при вызове функции.
Значения выражений- фактических параметров заносятся в участки памяти, выделенные для формальных параметров функции. При этом float преобразуется в double, a char, short int - в тип int.
В теле функции выполняется обработка с использованием значений внутренних объектов-параметров, и результат передается в точку вызова функции как возвращаемое ею значение.
Никакого влияния на фактические параметры функция не оказывает.
Возвращение значений
Напишем функцию, вычисляющую абсолютную величину числа. Абсолютная величина числа - это его значение, если отбросить знак. Например, абсолютная величина 125 - это 125, а абсолютная величина числа ( -125 ) - это тоже 125. Назовем эту функцию abs( ). Входом для этой функции будет любое число, для которого мы хотим найти абсолютную величину. Выходная величина возвращается при помощи ключевого слова языка Си - return. Поскольку функция abs( ) должна быть вызвана другой функцией, мы создадим простую функцию main( ), основной целью которой будет проверка, работает ли функция abs( ). Программа, спроектированная для того, чтобы проверить работу функции именно таким образом, называется драйвером. Драйвер подвергает функцию последовательным проверкам. Если результаты оказываются удовлетворительными, то ее можно поместить в программу, заслуживающую большего внимания. Термин драйвер обычно относится к программам, управляющим работой устройств:
/*драйвер*/
int main( )
{
int a=100, b=0, c=-122;
int d,e,f;
d=abs(a); e=abs(b); f=abs(c);
printf("%d, %d, %d\n",d,e,f);
}
int abs(int x) /* функция, вычисляющая величину числа */
{
int y;
y = (x < 0) ? -x : x;
/*возвращает значение y вызывающей программы*/
return(y);
}
Результат работы программы выглядит так:
100 0 122
Ключевое слово return указывает на то, что значение выражения, заключенного в круглые скобки, будет присвоено функции, содержащей это ключевое слово (оператор). Поэтому, когда функция abs( ) впервые вызывается драйвером, значением abs(a) будет число 100, которое затем присваивается переменной d.
Переменная y является внутренним объектом функции abs( ), но значение y передается в вызывающую программу с помощью оператора return. Действие, оказываемое оператором
d=abs(a);
по другому можно выразить так:
abs(a);
d=y;
Такой записью мы воспользоваться не можем! Вызывающая программа даже не подозревает о том, что переменная y существует.
Оператор return оказывает и другое действие. Он завершает выполнение функции и передает управление следующему оператору в вызывающей функции. Это происходит даже в том случае, если оператор return является не последним оператором тела функции:
/* Функция, вычисляющая абсолютную величину числа, вторая версия */
int abs(int x)
{
if(x < 0)
return(-x);
else
return(x);
}
Эта версия программы проще. Для пользователя обе версии неразличимы, т. к. у них имеется один и тот же вход, и они обеспечивают один и тот же выход. Только внутренние структуры обеих функций различны.
/* третья версия функции abs( ) */
int abs(int x)
{
if(x < 0) return(-x);
else return(x);
printf("Работа завершена!\n");
}
Наличие оператора return препятствует тому, чтобы оператор печати когда-нибудь выполнился в программе.
Можно пользоваться оператором
return;
Его применение приводит к тому, что функция, в которой он содержится, завершает свое выполнение и управление возвращается в вызывающую функцию. Поскольку у данного оператора отсутствует выражение в скобках, никакое значение не передается функции.
Все функции в программе, написанной на языке Си, равноправны. Каждая из них может вызывать любую другую функцию и, в свою очередь, каждая может быть вызвана любой другой функцией. Это делает функции языка Си несколько отличными от процедур Паскаля, поскольку процедуры в Паскале могут быть вложены в другие процедуры, причем процедуры, описанные в разных процедурах, являются недоступными для процедур, описанных в других независимых процедурах.
У процедуры main есть специфика. Она заключается в том, что после сборки программы, состоящей из нескольких функций, ее выполнение начинается с первого оператора функции main( ).
Каждая функция может вызвать саму себя. Действие, состоящее в том, что функция вызывает сама себя, называется рекурсией.
Рекурсивной называют функцию, которая прямо или косвенно сама вызывает себя. Именно возможность прямого или косвенного вызова позволяет различать прямую или косвенную рекурсию. При каждом обращении к рекурсивной функции создается новый набор объектов автоматической памяти, локализованных в теле функции. Функция называется косвенно рекурсивной в том случае, если она содержит обращение к другой функции, содержащей прямой или косвенный вызов определяемой (первой) функции. В этом случае по тексту определения функции ее рекурсивность (косвенная) может быть не видна. Если в теле функции явно используется вызов этой же функции , то имеет место прямая рекурсия.
! |
Так как при каждом обращении к рекурсивной функции создается новый набор объектов автоматической памяти, локализованных в теле функции, то при использовании рекурсивных алгоритмов с глубокой вложенностью рекурсии может быстро произойти переполнение стека реализации рекурсий, поэтому надежнее использовать итерационные алгоритмы. Например, пусть нужно реализовать "салфетку Серпинского" (геометрический фрактал). Как она образуется? Рисуется треугольник и в нем средние линии. В образованных при углах исходного треугольника новых треугольниках опять рисуются средние линии и так далее до заданного порядка вложенности рекурсии. Полученная "салфетка Серпинского" допускает другое, не рекурсивное, построение с помощью моделирования методом Монте-Карло. |