- •Алфавит языка Си
- •Идентификаторы (имена) языка Си
- •Ключевые слова
- •Константы (литералы)
- •Комментарии
- •Пример программы на Си
- •Последовательность обработки программы на Си
- •Термины
- •2.1. Концепция типа данных
- •2.2. Базовые типы данных
- •2.2. Атрибуты объектов
- •2.2.1. Класс памяти
- •2.2.2. Область (сфера) действия, связанного с объектом идентификатора
- •2.2.3. Область видимости объекта
- •2.2.4. Продолжительность существования объекта (время жизни)
- •2.2.5. Тип компоновки (связывания)
- •2.3. Объявления, определения и описания в языке Си
- •2.4. Объявления переменных стандартных типов
- •2.5. Преобразования типов
- •Термины
- •3.1. Определение и общая классификация операторов
- •3.2. Последовательно выполняемые операторы
- •3.3. Операции языка Си
- •3.4. Приоритеты и порядок выполнения операций, тип результата выражения
- •Тип результата выражения арифметической бинарной операции
- •Даны X, y (значения вводятся с клавиатуры). Вычислить a, если
- •Термины
- •4.1. Условный оператор
- •4.2. Переключатель
- •4.3. Операторы циклов
- •4.4. Оператор безусловной передачи управления
- •4.5. Оператор возврата из функции
- •4.6. Оператор выхода из цикла или переключателя
- •4.7. Оператор перехода к следующей итерации цикла
- •Термины
- •5.1. Указатели
- •5.2. Ссылки
- •5.3. Массивы
- •5.3.1. Одномерные массивы
- •Int a[100]; // Объявление массива из 100 элементов типа int
- •Инициализация массива
- •Массивы и указатели
- •Динамические массивы
- •5.3.2. Многомерные массивы
- •Инициализация многомерных массивов
- •Динамические многомерные массивы
- •Указатель на массив указателей и указатель на массив
- •Int ** p1; // Указатель на массив указателей
- •Int (*p2)[10]; // Указатель на массив из 10 элементов типа int
- •Термины
- •6.1. Структуры
- •Int X; // Поле структуры
- •Int X; // Поле структуры
- •Int X; // Поле структуры
- •Инициализация структуры
- •Int year, page; // Год издания и число страниц
- •6.2. Объединения
- •6.3. Битовые поля структур и объединений
- •Int a1: 10; // Битовое поле из 10 бит
- •Int a2: 14; // Битовое поле из 14 бит
- •Int :6; // Пропускаем 6 бит
- •6.4. Директива определения типа typedef
- •Термины
- •7.1. Определение, описание и вызов функции
- •7.2. Использование значений параметров по умолчанию
- •7.3. Изменение значений скалярных параметров в функциях
- •7.3.1. Передача параметров в функции по значению
- •7.3.1. Передача параметров в функции по указателю (по ссылке)
- •7.4. Передача в функцию массивов
- •7.4.1. Передача в функцию массивов, не являющихся строками
- •7.4.2. Передача в функцию строк
- •7.5. Функции с переменным количеством параметров
- •7.6. Рекурсивные функции
- •7.7. Подставляемые (inline) функции
- •7.8. Указатели на функции
- •7.10. Шаблоны функций
- •7.11. Возможные параметры функции main
- •Термины
- •8.1. Общие сведения о вводе-выводе
- •8.2. Стандартные потоки ввода-вывода
- •8.3. Функции для вывода в поток stdout
- •8.4. Функции для считывания из потока stdin
- •8.5. Ввод- вывод в файлы Открытие файла
- •Закрытие файла
- •Определение конца файла
- •Функции записи в файл
- •Функции чтения из файла
- •Функции позицирования в файлах
- •Функции для сброса буферов
- •Термины
- •9.1. Стадии и основные команды препроцессорной обработки
- •9.2. Замены в тексте
- •9.3. Макроподстановки
- •9.4. Включение текстов из файлов
- •9.5. Условная компиляция
- •9.6. Директива нумерации строк
- •9.7. Директива обработки ошибок
- •9.8. Директивы # и ##
- •9.9. Заранее определенные препроцессорные идентификаторы
- •9.10. Указания компилятору
- •Термины
7.6. Рекурсивные функции
Функция является рекурсивной, если вызывает саму себя. Рекурсия может быть прямой и косвенной. Прямая рекурсия – функция вызывает саму себя явно. Косвенная рекурсия – функция вызывает саму себя через другие функции. Ниже представлен классический пример рекурсивной функции для вычисления факториала.
#include <stdio.h>
int fact(int a) // Рекурсивная функция для вычисления факториала
{
if (a<0) return 0;
if (a==0) return 1;
return a*fact(a-1); // Вызов функции внутри себя
}
void main()
{
printf("fact(5)=%d", fact(5)); // Печатается fact(5)=120
}
Следует отметить, что рекурсивные функции необходимо применять аккуратно, так как в случае ошибок возможна бесконечная последовательность рекурсивных вызовов. Считается, что рекурсию не следует применять, если есть явное итерационное решение, например, для вычисления факториала, приведенный выше пример является число демонстрационным. Но существуют некоторые классы задач, для которых использование рекурсивных функций является удобным.
7.7. Подставляемые (inline) функции
Подставляемая функция это функция, определяемая с модификатором inline. Для обычной функции код функции существует в единственном экземпляре не зависимо от количества ее вызовов. Для подставляемой функции код функции существует в стольких экземплярах, сколько раз функция вызывается. Компилятор подставляет исполняемый код функции в точку вызова. Недостаток подставляемых функций - объем исполняемого кода программы возрастает, преимущество – возможен некоторый выигрыш в быстродействии, так как не требуется время на передачу управления на код функции при ее вызове.
Подставляемые функции появились в языке Си++, в исходном языке Си их не было.
Существует некоторые ограничения на использование подставляемых функций. Функция не может быть подставляемой:
-
функция слишком велика;
-
функция рекурсивная;
-
обращение к функции идет до ее определения;
-
функция вызывается более 1 раза в одном выражении;
-
функция имеет цикл, переключатель или оператор goto.
При невыполнении перечисленных ограничений ошибки не будет, просто компилятор будет считать данную функцию не подставляемой, некоторые компиляторы могут при этом выдавать предупреждение. Обычно подставляемые функции являются небольшими, их основное назначение – вызов других функций. Ниже представлена подставляемая функция для вычисления длины вектора на плоскости.
#include <stdio.h>
#include <math.h>
inline double getVect(double x, double y)
// Функция считает длину вектора, заданного точкой x, y
{
return sqrt(x*x+y*y);
}
void main()
{
printf("%f", getVect(3., 4.));
7.8. Указатели на функции
Имя функции, используемое без круглых скобок, воспринимается как значение указателя на функцию, содержащее адрес размещения кода функции в оперативной памяти. Данное значение можно присваивать указателю соответствующего типа и вызывать функцию по указателю. Достоинство: с помощью одного указателя можно вызывать разные функции, настраивая его динамически, а также передавать в качестве параметра в другую функцию.
Формат определения указателя на функцию:
<тип_возвращаемого_значения> (* <имя_указателя>) (<спецификация_формальных_параметров>);
При определении указателя имена параметров можно не использовать, главное, чтобы совпадало: тип возвращаемого значения, количество параметров и их типы.
Ниже представлен пример использования указателей на функции. В программе вводятся с клавиатуры два вещественных значения, знак операции (+, -, *, /), между значениями выполняется введенная операция, результат выводится на печать. В зависимости от введенного знака операции указатель на функцию в программе настраивается на одну из функций.
#include <stdio.h>
double summa(double x, double y) // Функция вычисляет сумму параметров
{
return x+y;
}
double mult(double x, double y) // Функция вычисляет произведение параметров
{
return x*y;
}
double div(double x, double y) // Функция вычисляет результат деления параметров
{
return x/y;
}
double razn(double x, double y) // Функция вычисляет разность параметров
{
return x-y;
}
void main()
{
double x, y, Rez; // Два исходных параметра и результат операции
char Ch; // Символ определяет знак выполняемой операции
double (*pFun)(double, double); // Указатель на функцию
printf("x="); scanf("%lf", &x); // Ввод x
printf("y="); scanf("%lf", &y); // Ввод y
printf("Ch="); fflush(stdin); // Сброс буфера ввода
scanf("%c", &Ch); // Ввод знака операции с клавиатуры
switch(Ch) // В зависимости от знака операции настраиваем указатель
// на одну из функций
{
case '+': pFun=summa; break;
case '-': pFun=razn; break;
case '*': pFun=mult; break;
case '/': pFun=div; break;
default: printf("Error!"); return;
}
Rez=(*pFun)(x, y); // Вызов функции через указатель
printf("Rez=%f", Rez);
}
7.9. Перегрузка функций (Си++)
Перегрузка функций – это использование одного имени для разных функций. При этом функции должны отличать своей сигнатурой (количеством параметров или их типами).
Перегрузка функций появилась в языке Си++, в исходном языке Си такой возможности не было. Ниже представлен пример перегрузки функций.
#include <stdio.h>
int summa(int a, int b) // Функция считает сумму 2-х параметров типа int
{
return a+b;
}
int summa(int a, int b, int c) // Функция считает сумму 3-х параметров типа int
{
return a+b+c;
}
double summa(double x, double y) // Функция считает сумму 2-х параметров типа double
{
return x+y;
}
void main()
{
int s1, s2;
double s3;
s1=summa(2, 3); // Вызывается первая функция
s2=summa(5, 5, 1); // Вызывается вторая функция
s3=summa(1., 2., 1.5); // Вызывается третья функция
printf("s1=%d s2=%d s3=%f", s1, s2, s3);
}