КАК управлять стихией?


Мы уже знаем немало - умеем получать числа с клавиатуры и подставлять их в математические формулы, вводить и выводить своё имя; знаем что такое символ, и как из радиан сделать градусы. Но тут же встаёт вопрос: КАК диктовать компьютеру свою непреклонную волю? КАК ставить ему такие условия, от которых он просто не сможет отказаться? КАК заставлять его что-то делать несколько раз подряд? Ведь даже для той же математики одного знания формул мало.

В каждом языке программирования, будь то Бейсик, Паскаль, Фортран, Ява или наш Си, есть определённые управляющие структуры - а куда деваться? Без них вряд ли что-то можно было сделать. И сейчас мы с ними познакомимся.

КАК поставить условия, от которых компьютер не сможет отказаться. (Условие if/else)
КАК скакать по веткам и не поцарапаться (Операторы switch/case)
КАК заставить компьютер упасть и отжаться десять раз? (Цикл for)
КАК сказать компьютеру: "Ты не пойдёшь гулять, пока не съешь всю кашу?" (Цикл while)
КАК ехать не осле, держа перед ним морковку? (Цикл do/while)

Вопрос: КАК поставить условия, от которых компьютер не сможет отказаться. (if/else)

Вот простой пример. У нас есть три числа, и мы хотим найти из них самое маленькое. Можно предположить, что для этого их все нужно сравнить между собой, и тогда мы найдём минимум. Для сравнения чисел в программировании применяются условия. Блок условия прост. Если условие истина, выполнится блок в фигурных скобках.

if( A > B)
{

printf(" A > B ");
}

В данном примере, если A>B на экране появится: "A>B". Если А < B или А=B, на экране не будет ничего. С точки зрения логики блок условия реализует схему: "ЕСЛИ - ТО".

Теперь вернёмся к нашему примеру. Для того, чтобы найти меньшее из 3-х чисел, нужно сравнить их между собой. Можно предположить, что для этого потребуется три условия. Но с точки зрения программирования это не совсем изящно и совсем не экономично.

Действительно!

Вместо того, что три раза сравнивать эти числа, можно использовать всего два сравнения. Как?

Путь у нас есть три числа: A, B, C, значения которых нам заранее не известны. Введём дополнительную переменную, которую назовём MIN - минимальное число. Не мудрствуя лукаво, предположим, что изначально MIN = A, тогда если B < MIN, тогда MIN = B, так как оно всегда должно быть наименьшим. Сранив теперь MIN c C, мы узнаем кто из них меньше, и если C < MIN, то MIN = C. Так всего с двумя условиями мы решили вопрос положительно!

Вот ещё, - скажете вы, - зачем ломать голову? Ведь можно решить просто написать три раза условие и не думать над такой мелочью...

Можно-то можно, но любая дополнительная операция (в данном случае условие), как говорят программисты, "жрёт" память. Это не имеет значения, если в нашем распоряжении 128 МБ оперативной памяти. Но если мы программируем микроконтроллер, в памяти которого должны уместиться адреса, данные и стек, а нам даны "на всё про всё" 16 Кб, приходится дрожать над каждой строчкой кода, чтобы хоть как-то влезть в этот лимит.
Хорошо, господа! Теперь посмотрим, как можно решить ту же задачу на языке СИ.

#include<stdio.h>

main()
{
float a, b, c, min; //объявляем 4 вещественных числа

printf("Введите три числа: ");
scanf("%f %f %f", &a, &b, &c); //вводим их с клавиатуры

min=a; //допустим, что min=a

if (b < min)
min=b; //эта строчка выполняется если условие истино

if (c < min)
min=c;

printf("Минимальное число %.2f", min);
}


Не правда ли изящная программка? Минимум кода при максимуме результата!

Ну ладно, это всё детские игрушки! Переходим к алгебре. Вычислим корни квадратного уравнения. Задача простая, сто раз на бумажке считали. Теперь мы хотим её автоматизировать. Напомню, что квадратное уравнение имеет вид: ax^2+bx+c = 0. Из-за квадрата у нас два корня x1и x2. Формулы нам давно известны, нас же интересует сейчас такая вещь, как дискриминант. Если он меньше нуля решений нет. Если мы это не отследим, то можем невзначай вылететь из программы, которая получит сообщение об ошибке - "Извлечение корня из отрицательного числа". Чтобы не было потом мучительно больно мы должны "предупредить" это.

#include<stdio.h>
#include<math.h> //не забываем про математическую библиотеку

main()
{

float a, b, c, x1, x2, d;

printf("Введите коэффициенты: ");
scanf("%f %f %f", &a, &b, &c); //вводим числа

d = b*b - 4*a*c; //формула вычисления дискриминанта D=b^2 - 4*a*c

if(d>0) //если дискриминант положительный, то
{
x1 = (- b - sqrt(d)) / (2*a); //вычисляем x1
x2 = (- b + sqrt(d)) / (2*a); //вычисляем x2

printf("x1 = %.2f", x1); //выводим их на экран
printf("\nx2 = %.2f", x2);
}
else // если d <=0, то

if(d==0) //если d = 0
{
x1 = -b/(2*a);
printf("\nx = %.2f", x1); //корень будет только один
}

else
//если d < 0
printf("\nИзвините, корней нет!");

}

Здесь надо обратить внимание на две вещи:

1. Конструкция условия. Здесь вы неожиданно для себя нашли незнакомое слово "else". В прошлом примере у нас не было else, оно было не нужно. Else означает "иначе". Применять его надо тогда, когда мы хотим, чтобы что-то выполнялось, когда условие в скобках ложно. Рассмотрим маленький фрагмент:

if (hours < 17) {
printf("Добрый день");
}
else
{
printf("Добрый вечер!");
}


Если текущее время меньше 17 часов, программа скажет нам "Добрый день", если больше или равно 17 - это уже "Добрый вечер". Таким образом алгоритм программы разветвляется и в зависимости от времени суток идёт по разным веткам. Примерно так работает интерфейс WEB-сайтов, когда с вами здороваются в зависимости от времени суток.

В принципе в данном примере можно было не ставить фигурные скобки, ведь по умолчанию вслед за условием в случае его истиности гарантированно выполнится следющая строка кода. То же самое и с else. Но я советую вам ставить скобочки в любом случае. Часто приходится что-то добавлять и скобочки ставить всё-равно придётся, а если и нет, читаемость такого кода будет гораздо выше.

Если, если мы пока не знаем что будет в этих скобках, можно поставить несколько точек с запятой. Таким образом мы сымитируем наличие какой-то операции.

if (hours < 23) {

;; //пустая операция

}
else {

printf("Доброй ночи!");
}


Иногда, при отладке программы, когда программист хочет показать, что этот код должен выполняться всегда (не столько компилятору, сколько другим программистам, которые будут читать этот код), он может написать так:

if (1) {

... //какой-то код
}

В случае, если мы хотим, чтобы код не выполянлся никогда, мы можем написать так:

if(0) {

... //этот код никогда не будет работать

}


Последняя запись - аналог коментария. Помещение кода в эти фигурные скобки равносильно тому, что вы их /*закоментировали*/. Хорошие редакторы кода подсвечивают такую запись тем же цветом, что и коментарии - неактивный код.

Блоки if и else вы можете конфигурировать достаточно свободно. Например конструкция if / else / if, когда в блок else мы помещаем ещё один оператор условия. Самое главное, чтобы такая запись не загромождала код, и вы сами смогли бы его прочитать.

Условия языка Си:

Мы рассмотрели примеры, где числа сравниваются строго - ">" больше или "<" меньше. В условиях же используются и другие операторы сравнения:

==
равно. Знак равенства пишется дважды, чтобы не путать с присваниванием.
>, < больше, меньше
>=, <=
больше или равно, меньше или равно
!=
не равно

Вопрос: КАК скакать по веткам и не ободрать.... (switch/case)

Продолжая мысль начатую в прошлой главе, можно предположить, что нам может потребоваться ещё более разветвлённый алгоритм. Пусть мы хотим создать свой калькулятор. Мы ввели два числа и должны выбрать операцию: +, -, *, /. Можно конечно построить такую программу на основе условий if:

if (znak == '+') {

}

if (znak == '-') {


}

if(znak == '*') {


}

if(znak == '/') {


}

Представляете, как легко запутаться в программе где надо будет отследить 20 вариантов?

Гораздо удобнее в этом случае использовать оператор ветвления switch/case.

#include<stdio.h>

main()
{
float a, b, s;
char znak; //символьная переменная, в которой будет храниться знак

printf("Введите два числа: ");
scanf("%f %f", &a, &b);

printf("Введите знак действия: ");
scanf("%c", &znak); //не забудьте указать, что znak - символ и поставить %c

switch(znak) { //запоминаем знак

case '+' :
s = a+b;
printf(s);
break; //если знак +, ответ s= а+b, выводим ответ на экран

case '-' :
s = a-b;
printf(s);
break;

case '*' :
s = a*b;
printf(s);
break;

case '/':
s = a/b;
printf(s);
break;

default : printf("\nВы ввели что-то не то!"); //если пользователь ввёл что-то другое
}
}


Символьная переменная znak хранит действие, которое мы хотим выполнить. Получив знак с клавиатуры, мы хотим организовать ветвление, чтобы выполнить соответствующую операцию. Запомнив переменную znak с помощью switch ("защёлка"), мы обрабатываем её варианты с помощью оператора case. Действия выполняемые в блоках case даже не нужно выделять в фигурные скобки. Для того, чтобы завершить блок, мы пишем в конце команду break. Эта операция означает звершение текущего модуля, то есть как только мы его встречаем в текста, мы вываливаемся за пределы квадратных скобок, в которых мы находимся. Так, после выполнения данного case мы, натолкнувшись на break, выходим за предеды данного блока switch.

Сразу же возникает резонный вопрос - а что будет, если пользователь введёт не какое-нибудь из четырёх действий, а что-то совершенно другое - букву или цифру? Мы уже говорили с вами про "защиту от дурака", и один из способов - оценить то, что он ввёл и вернуть его на этап ввода. Ещё один вариант - создать ветвь default ("по умолчанию"), по которой пользователь отправится, если не введёт то, что предусмотрено нашими case. Таким образом, в данном случае,
default - это реакция на неправильные действия, которые не предусмотрены нашим алгоритмом.

А что нам делать, если мы хотим чтобы одна и та же ветвь работала сразу для нескольких случаев? Например, вы спрашиваете пользователя - хочет ли он покинуть программу. Ответ - нажатие Y - да, N - нет. Но если у пользователя включена клавиша Caps Lock символы будут иметь уже другой код и никуда он не выйдет. Можно предусмотреть этот случай! Совсем не обязательно писать ветвь отдельно для больших и маленьких букв. Просто не нужно ставить команду break. Например так:

printf("Вы хотите выйти из программы? (Y/N)");
scanf("%c", &answer);

switch (answer) {

case 'Y':
case 'y':
printf("До свиданья!");

break;

case 'N':
case 'n':
printf("Продолжаем работу!");

break;


}
+

Хочется отметить, что эта управляющая структура очень популярна в игровых программах. Когда мы двигаем по полю нашего колобка, поедающего вишенки и бананы, мы нажимаем клавиши со стрелками. Задача программы - определённая реакция на нажатие каждой клавиши. Операторы switch/case там просто не заменимы, и мы обязательно рассмотрим такой пример!

Вопрос: КАК заставить компьютер упасть и отжаться десять раз?

Мы приближаемся к циклам! Вообще цикл - многократное повторение чего-либо. Пусть мы хотим вывести на экран какое-то слово не один раз, а десять. Можно написать 10 раз printf, а можно задать цикл - "Сделай то, что в скобках 10 раз". Программка получится махонькая, а результат тот же.

#include<iostream.h>

void main()
{
int i; //переменная - счётчик

char *slovo="Здравствуй, мир!"; //объявляем строку с фразой

for(i=1; i<=10; i++) //а это цикл
cout<<slovo<<endl;
}


Цикл for используется тогда когда мы заранее знаем, сколько раз мы хотим повторить что-либо. Объявив переменную i мы делаем её счётчиком. В скобках цикла for мы показываем, что она меняется от 1 до 10 с шагом 1.
Кстати что такое i++? Не правда ли, похоже на С++? В С++ и многих других С-подобных языках используются сокращённые операции. Операция ++ называется операция "инкремента" и соответствует увеличению числа на 1. Соответственно -- "декрмент" и являет собой уменьшение на 1.

Таким образом,
i++ это тоже самое, что i = i+1
i-- не что иное, как i=i-1

Кроме того есть ещё несколько форм записи, например:

i+=2 это i=i+2, а
i-=3 это i=i-3

Язык С был придуман Деннисом Ритчи, тогда как автор С++ - Бьорн Страуструп. Чтобы показать всем программистам, что С++ - надмножество, расширение языка С он назвал его на понятном всем программистам языке С++, то есть с+кое что ещё!


Чтобы закрепить, найдём среднее арифметическое n чисел. Kомпьютер найдёт их сумму и разделит на общее количество.

#include <iostream.h>

main()
{
int n, s = 0;

for (int i = 1; i < 50; i++) {
s = s+i;
}
cout<<"Среднее арифметическое: "<<(s / i);
}

Мы последовательно складываем 1+2+...+49, а затем сумму делим на i - общее число элементов.

Мы уже сказали несколько слов о массивах, когда затронули тему строк и указателей. Число элементов массива мы знаем почти всегда, поэтому цикл for нам может здесь очень пригодиться. Однако, мы не всегда знаем сколько раз надо сложить числа. Иногда нужно поставить условие "ПОКА". Например, i = i + 1, ПОКА i не достигнет J. Здесь нам на помощь приходят другие циклы.

Вопрос: КАК сказать компьютеру: "Ты не пойдёшь гулять, пока не съешь всю кашу?" (Цикл while)

Пример.
Пусть мы хотим вывести на экран все нечётные числа от 0 до числа n, которое вводится с клавиатуры и заранее не известно.

#include<stdio.h>

main()
{
int x = 1, n;

printf("Введите число больше 1: ");
scanf("%d", &n);

while (x < n) //пока число x не достигнет предела n
{

printf("x = %d\n", x); //выводим результат на экран
x += 2; //увеличиваем х на 2

}

}

Эта удивительно простая программа позволяет понять цикл while. Пока условие в скобках не станет ложным, на экран будут выводиться всё новые значения x.

Но если пользователь введёт отрицательное значение введёт 2 или 3, работа программы будет не интересной. Мы увидим только одну строчку: "x = 1". Сейчас мы используем цикл while для создания так называемой "защиты от дурака". Пусть если пользователь вводит значения меньшие или равные 3, программа будет предлагать ему ввести их заново!

#include<stdio.h>

main()
{
int x = 1, n;

printf("Введите число больше 3: ");
scanf("%d", &n);

//Пока n <=3 пользователь будет вводить число заново
while (n <= 3) {

printf("Введите число больше 3: ");
scanf("%d", &n);

}

while (x < n) //пока число x не достигнет предела n
{
printf("x = %d\n", x); //выводим результат на экран
x += 2; //увеличиваем х на 2
}
}

Недостаток этой программы только один. Нам два раза пришлось писать команды ввода числа - в первый раз и в цикле. Это не экономично. Но цикл while сначала проверяет, а потом выполняется, поэтому мы не можем убрать приглашение в самый первый раз. Вот если бы можно было сначала выполнять цикл, а потом проверять условие! Такая конструкция в языке СИ есть!

Цикл do / while отличается от цикла while тем, что в нём истинность условия проверяется уже после выполнения цикла, а не перед ним. Другими словами, тело цикла гарантированно будет выполнено хотя бы один раз!

Вопрос: КАК ехать на осле, держа перед ним морковку? (Цикл do/while)

Пример:
Пусть мы хотим сложить все чётные числа от 0 до числа n, которое вводится с клавиатуры.

#include<stdio.h>

main()
{
int i = 0, sum = 0, n;

printf("nВведите n: ");
scanf("%d", &n);

do {

i +=2;

printf("%d + %d= ", sum, i);
sum = sum+i;

printf("%d\n", sum);
}while(i < n); //пока i<n

printf("Сумма четных чисел от 0 до %d = %d", n , sum);

}

Цикл while используется очень широко не только в математических алгоритмах. Его свойство широко используется для создания визуальных эффектов.

Пусть нам нужно, чтобы по экрану бежала строка, пока пользователь не нажмёт любую клавишу. Функция kbhit() - то, что нам нужно! kbhit - это не "хит конструкторского бюро", это "keyboard hit" - нажатие клавиатуры, которое она отслеживает. Её значение равняется 1, если пользователь нажал любую клавишу и 0 если нажатия в данный момент не было.

Поскольку мы изучаем циклы, постараемся использовать их по полной программе. Посмотрим, как можно циклически менять значения цвета, звука и координат текста. Заодно мы рассмотрим новую для нам библиотеку conio.h ("console input output" - консольный ввод-вывод). Консолью в стародавние времена назывались терминалы - гибриды клавиатуры и монитора. И консольный ввод-вывод - это работа функции сугубо для работы с монитором и клавиатурой. Вывод текста в цвете в текстовом режиме, обработка нажатия клавиш и ещё много другое.

/*Программа "бегущая строка*/

#include<stdio.h>
#include<conio.h> //новая библиотека для работы с текстом
#include <dos.h>

main()
{
int a = 10;

//пока не нажата клавиша

while(!kbhit()) {

clrscr(); /*очищаем экран*/
sound(50*a); /*выдаем звуковой сигнал, частота котоpого в скобках*/
textcolor(a); /*задаём цвет текста*/
gotoxy(a,10); /* задаём координату текста. Кооpдината по х меняется*/

/*кооpдината по у всегда pавна 10 */

cprintf(" Привет !!! :o)"); /*Вывод текста в цвете*/

nosound(); /*Выключить звук*/
delay(1000); /*Если у вас мощный компьютер, поставьте задержку побольше*/

a++;
}
getch();
}

Слово "Привет!!!" будет перемещаться по экрану, пока вы не нажмёте на какую-нибудь клавишу. В качестве эксперемента попробуйте произвольно изменить некоторые числа в программе и посмотрите что из этого получится.

Здесь мы встретили множество новых функций:

Название функции
Описание
clrscr()
Очистика экрана в текстовом режиме
getch();
Ожидание нажатие клавиши
gotoxy(x,y);
Переместить курсор в координату x, y
textcolor(color);
Установить color как цвет текста для функции cprintf
cprintf();
Color printf - вывод текста в цвете, по умолчанию работает, как printf
textbackground(color);
Устанавливает цвет фона символа для функции cprintf
sound(freq);
Выводит звук с частотой freq через PC Speaker
delay(ms);
Задержка выполнения программы. Аргумент в милисекундах
nosound();
Отключает поток звука, выводимый на PC Speaker


Вторая библиотека, которую мы здесь используем dos.h. Во времена DOS она позволяла реализовать большинство сервисных функций MS-DOS на достаточно низком уровне. Она охватывала собой довольно широкий спектр областей использования (работа с жёстким диском, файлами, генерация прерываний, пс-спикер) и использовалась очень широко. Современные компиляторы типа Microsoft Visual C++ не поддерживают библиотеки для работы в DOS (и частично функции conio.h), поэтому, чтобы скомпилировать такую программу нужен 16-разрядный компилятор Borland C++ 3.1, который полностью эмулирует работу MS-DOS.

Как функции для работы с цветным текстом, так и программирование звука через системный динамик широко использовалось в 80-е годы создателями игр. Творились настоящие чудеса! Через динамик выводились целые симфонии, увертюры и сонаты. Сотни произведений классиков было переложено в код. Находились умельцы, которые с большой точностью воспроизводили целые слова человеческой речи подбирая соответствующие частоты. С появлением звуковых карт, файлов WAV и MIDI, работа с PC-спикером сошла на нет. В DOS воспроизведение WAV файлов очень сложное дело, так как к программе нужно подключать драйвер, настривать каналы прямого доступа к памяти и порты запроса на прерывание. Приходилось комплектовать игры набором драйверов для всех популярных звуковых карт, существовавших на тот момент. В Windows система уже настроена на вашу звуковую плату, и программируя звук, вы просто даёте команду: воспроизвести файл. Система всё сделает за вас!

Преждевременное прерывание цикла и аварийный выход

Иногда нужно прервать цикл до его завершения или завершить программу в её определённой точке. Например если в процессе цикла вы проверяете данные и обнаруживаете ошибку нет никакого смысла ждать его завершения (которое кстати может иногда вообще не наступить в виду изначальной некорректности данных!). Есть много разных способов решить проблему, и сейчас мы с ними ознакомимся:

1. Метка

На самом деле метка - "тяжёлое наследие" языка Ассемблер, над которым Си, в своё время, был надстроен. В языках высокого уровня этот способ считается "плохим стилем" программирования. Считается также, что любую проблему можно решить одними циклами и условиями, никогда не прибегая ни к каким "чёрным меткам". Тем не менее, несмотря на всеобщее осуждение, мне неоднократно приходилось встречать метки даже в очень серьёзных исходниках. Так что осуждать-осуждают, но используют, правда, без лишнего шума...

Метку можно поставить в любом месте исполнимого кода, в качестве точки, на которую по команде goto можно будет перепрыгнуть из любого места программы. Дальнейшее выполнение программы продолжится с этого места. Итак, как это выглядит:

#include<stdio.h>

void main()
{
int a=100;

while (a>1) {

printf("Input A > 0: ");
scanf("%d", &a);

if(a<=0)
goto m;
else
printf("A = %d\n", a);
}

//Метка за пределами цикла
m:
printf("\nПо метке вы выходите из цикла");
}

Метка может находиться как выше, так и ниже команды goto. Её не нужно объявлять, как переменную.

В отличие от СИ, в языке BASIC метки распространены очень широко. Как правило при программировании на этом языке нумеруются строки, и очень распространён переход с помощью goto на строку с определённым номером. В языке, где не выражены процедуры и функции это приемлемо и это единственный выход сократить объём кода.

2. Инструкция break

Этот способ несколько напоминает goto, только в данном случае адрес перехода не указывается. После break; выполняется следуюшая строка после тела цикла, независимо от того, сколько строк оставалось до конца. Мы с вами уже с знакомы с break ещё по разделу switch/case.

#include<stdio.h>

void main()
{
int a=100;

while (a>1) {

printf("Input A > 0: ");
scanf("%d", &a);

if(a<=0)
break;
else
printf("A = %d\n", a);
}
printf("\nВыход из цикла по break");
}

В отличие от меток, такая конструкция одна из наиболее применимых. Хорошим стилем программирования считается проверять аргументы, подаваемые на вход функции, и если они не соответствуют нужному типу, выходить из функции, чтобы программа не завершилась из-за деления на ноль или перполнения памяти.

3. Выход по return()

Функция return() возвращает значения. Возвращаемое значение 0 есть признак нормального завершения программы. Если программой возвращается значение отличное от 0, это называется аварийным завершением программы.

If (a==10)
return(0); //выход из программы


Тело программы main() тоже функция, причём обязательная для любой программы. Она может быть "пустой" - типа void, а может возвращать значения. В первом случае, мы должны объявить её как:

void main()
{

}


В другом случае, она может возвращать значения, и тогда необходимо присвоить ей тип:

int main()
{

return 0; //признак нормального завершения программы
}


Чаще всего, инструкция return используется в функциях, для того, чтобы определить тип возвращаемого значения. В данном случае, нам нет необходимости передавать какое-то число. Мы можем использовать инструкцию return и саму по себе. В этом случае, её вызов будет аналогичен инструкции break

#include<stdio.h>

void main()
{
int a=100;

while (a>1) {

printf("Input A > 0: ");
scanf("%d", &a);

if(a<=0)
return;
else
printf("A = %d\n", a);
}
printf("\nВыход из цикла по return");
}

4. Инструкция continue

Интересна инструкция continue. Она заставляет программу пропустить все оставшиеся строки цикла, но сам цикл не прекращается.

#include<stdio.h>

void main()
{
int a=100;

while (1) {

printf("Input A > 0: ");
scanf("%d", &a);

if(a<=0)
continue;
else
printf("A = %d\n", a);
}

printf("\nВыход из цикла по continue");
}

4. Функция exit()

Эта инструкция прерывает не только цикл, но и саму программу. Используя константы, любезно предоставленные в stdlib.h, можно использовать инструкцию так:

exit(EXIT_SUCCESS);


в случае успешного завершения программы и

exit(EXIT_FAILURE);


в случае ошибки. Пример:

#include<stdio.h>
#include<stdlib.h>

void main()
{
int a=100;

while (1) {

printf("Input A > 0: ");
scanf("%d", &a);

if(a<=0)
exit(EXIT_FAILURE);
else
printf("A = %d\n", a);
}

printf("\nВыход из цикла по exit");
}


Вообще использование различных "говорящих" констант - очень хороший стиль программирования. Применяя их, вы сможете легко понять о чём идёт речь в вашем коде, даже, когда откроете его через 10 лет!

5. Функция atexit ()

Иногда по завершении программы мало просто возвратить ноль, как знак успешного конца. Например, при работе с динамической памятью, необходимо уходя очищать память, выделенную под переменные и объекты, иначе даже после выхода из программы они будут продолжать "висеть" в памяти до выключения компьютера. Для этой цели можно предусмотреть некую финальную функцию, которая будет выполнять некоторые обязательные действия, которыми должен сопровождаться любой выход из программы. В объектно-ориентированном программировании для этой цели служит деструктор, в процедурном можно применять функцию atexit, которая регистрирует функцию, которая будет запускаться при выходе. В качестве аргумента она принимает указатель на эту функцию. На сегодняшний день применение atexit() ограничено.

В приведённом ниже примере мы зарегистрируем сразу две функции выхода. Это необходимо, чтобы понять принцип работы atexit. Для выхода из цикла мы можем использовать break, exit или return, и всякий раз, до своего завершения, программа будет предварительно запускать функцию, зарегистрированная, как функция выхода через вызов atexit().

Можно зарегистрировать сразу нескольких функций выхода, при этом первой будет вызываться последняя зарегистрированная функция:

#include<stdio.h>
#include<stdlib.h>

void function1(void)
{
printf("До свиданья!\n");
}

void function2(void)
{
printf("Приходите ещё!\n");
}

void main()
{
int a=100;

atexit(function1);
atexit(function2);

while (1) {

printf("Input A > 0: ");
scanf("%d", &a);

if(a<=0)
exit(EXIT_SUCCESS);
else
printf("A = %d\n", a);
}

printf("\nВыход из цикла по exit");
}

Таким образом, перед выходом из программы мы увидим на экране сначала "Приходите ещё!" а потом "До свиданья!".


Назад Содержание Вперёд

Сайт управляется системой uCoz