ПРОГРАММА В ЦЕЛОМ
Программа в целом состоит из функций. Одна из функций должна иметь имя main(),
С ФУНКЦИИ main НАЧИНАЕТСЯ ВЫПОЛНЕНИЕ ПРОГРАММЫ.
(на самом деле этому предшествует отведение и инициализация глобальных переменных; смотри последующие лекции).
Часто main() - единственная функция в программе.
Структура программы такова:
#include /* магическая строка */
/* ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ (о них позже) */ int a = 7; int b; /* по умолчанию 0 */
/* ФУНКЦИИ */ f1(){....} f2(){....}
/* НАЧАЛЬНАЯ (ГЛАВНАЯ) ФУНКЦИЯ */ void main(){ ... }
Пример программы:
#include
int f1(int x, int y){ return (x + y*2); }
int f2(int x){ int z;
z = x+7; return 2*z; }
void main(){ /* Объявления переменных */ int a, b, c;
/* Операторы */ a = 5; b = 6;
c = f1(a, b+3); b = f1(1, 2); a = f2(c);
printf("A есть %d B есть %d C есть %d\n", a, b, c); }
Она печатает:
A есть 60 B есть 5 C есть 23
КАК НЕ НАДО ПРОГРАММИРОВАТЬ ЦИКЛЫ
int i;
for(i=0; i < 4; i++){ if(i == 0) func0(); else if(i == 1) func1(); else if(i == 2) func2(); else if(i == 3) func3(); }
В данном примере цикл АБСОЛЮТНО НЕ НУЖЕН. То, что тут делается, есть просто ПОСЛЕДОВАТЕЛЬНОСТЬ операторов:
func0(); func1(); func2(); func3();
Цикл имеет смысл лишь тогда, когда много раз вызывается ОДНО И ТО ЖЕ действие, но может быть зависящее от параметра, вроде func(i). Но не разные функции для разных i.
Аналогично, рассмотрим такой пример:
int i;
for(i=0; i < 10; i++){ if(i==0) func0(); else if(i == 1) func1(); else if(i == 2) func2(); else funcN(i); }
Тут funcN(i) берет на себя роль "а в остальных случаях". Однако, этот пример более естественно может быть записан так:
int i;
func0(); func1(); func2(); for(i = 3; i < 10; i++) funcN(i);
Заметьте, что цикл теперь начинается с индекса 3.
А теперь - случай, где смесь цикла и условного оператора оправдана:
int i;
for(i=0; i < 100; i++){ if((i % 2) == 0) even(); /* четный */ else odd(); /* нечетный */ }
Тут в цикле проверяется четность индекса i. C
/* Треугольник из звездочек */
#include
/* putchar('c') - печатает одинокий символ c */ /* символ \n - переводит строку */ /* nstars - сколько звездочек напечатать */
/* Функция рисования одной строки треугольника */ void drawOneLine(int nstars){ int i; /* номер печатаемой звездочки, счетчик */
for(i=0; i < nstars; i++) /* Рисуем nstars звездочек подряд */ putchar('*'); putchar('\n'); /* И переходим на следующую строку */ }
void main(){ /* ОБЪЯВЛЕНИЕ ПЕРЕМЕННЫХ */ int nline; /* номер строки */
/* ВЫПОЛНЯЕМЫЕ ОПЕРАТОРЫ (ДЕЙСТВИЯ) */ for(nline=1; nline <= 25; nline++) drawOneLine(nline); /* сколько звездочек? столько же, каков номер строки */ } C
/* Треугольник из звездочек */ /* Тот же пример со вложенным циклом, а не с функцией */
#include
void main(){ /* ОБЪЯВЛЕНИЕ ПЕРЕМЕННЫХ */ int nline; /* номер строки */ int i; /* номер печатаемой звездочки, счетчик */
/* ВЫПОЛНЯЕМЫЕ ОПЕРАТОРЫ (ДЕЙСТВИЯ) */ for(nline=1; nline <= 25; nline++){ /* сколько звездочек? столько же, каков номер строки */ for(i=0; i < nline; i++) putchar('*'); putchar('\n'); } } C
/* Треугольник из звездочек */ /* Теперь треугольник должен быть равнобедренным */
#include
/* nstars - сколько звездочек напечатать */ /* nspaces - сколько пробелов напечатать перед звездочками */
void drawOneLine(int nspaces, int nstars){ int i; /* номер печатаемой звездочки, счетчик */ /* он же - номер печатаемого пробела */
for(i=0; i < nspaces; i++) putchar(' '); for(i=0; i < nstars; i++) putchar('*'); putchar('\n'); }
/* n (номер строки) ...* 1 ..*** 2 .***** 3 ******* 4
Всего строк: LINES Число звездочек в n-ой строке: n*2 - 1 Число пробелов спереди (обозначены точкой): LINES - n
Все эти числа подсчитываются с картинки...
Их мы будем передавать в функцию drawOneLine в точке _вызова_, а не вычислять в самой функции. Функция для того и заведена, чтобы не вычислять ничего КОНКРЕТНОГО - все параметры ее переменные, и должны ПЕРЕДАВАТЬСЯ в нее из точки вызова.
В качестве параметра в точке вызова можно передавать не только значение переменной, но и значение выражения, то есть формулы.
*/ void main(){ /* ОБЪЯВЛЕНИЕ ПЕРЕМЕННЫХ */ int LINES = 25; /* всего строк. Это описание переменной сразу с ее инициализацией */ int nline; /* номер строки */
/* ВЫПОЛНЯЕМЫЕ ОПЕРАТОРЫ (ДЕЙСТВИЯ) */ for(nline=1; nline <= LINES; nline++) drawOneLine(LINES - nline, /* число пробелов --> nspaces */ nline*2 - 1 /* число звездочек --> nstars */ ); } C
/* Треугольник из звездочек */ /* Теперь треугольник должен быть равнобедренным */
#include
void drawOneLine(int nspaces, int nstars){ int i; /* номер печатаемой звездочки, счетчик */ /* он же - номер печатаемого пробела */
for(i=0; i < nspaces; i++) putchar(' '); for(i=0; i < nstars; i++) putchar('*'); putchar('\n'); }
void main(){ /* ОБЪЯВЛЕНИЕ ПЕРЕМЕННЫХ */ int LINES = 25; /* всего строк. */ int nline; /* номер строки */
/* Для человека естественно считать с 1. Для машины же первое число - это НУЛЬ. Поэтому цикл for(nline=1; nline <= LINES; nline++) Следует записать в виде for(nline=0; nline < LINES; nline++)
Он тоже выполнится 25 раз, но значение переменной-счетчика nline будет на каждой итерации на 1 меньше. Поэтому надо поменять расчет параметров для функции рисования.
n (номер строки) ...* 0 ..*** 1 .***** 2 ******* 3
Всего строк: LINES Число звездочек в n-ой строке: n*2 + 1 Число пробелов спереди (обозначены точкой): LINES - n - 1
*/
/* ВЫПОЛНЯЕМЫЕ ОПЕРАТОРЫ (ДЕЙСТВИЯ) */ for(nline=0; nline < LINES; nline++) drawOneLine(LINES - nline - 1, nline*2 + 1); } C
/* Тип переменных для хранения БУКВ называется
char
(от слова character).
Буквы изображаются в одиночных кавычках 'a' 'b' '+'.
Пример: char letter;
letter = 'a'; putchar(letter); letter = 'b'; putchar(letter); letter = '\n'; putchar(letter);
Символ '\n' обозначает "невидимую букву" - переход на новую строку, new line. Есть несколько таких специальных букв, о них - позже.
Зато сразу сделаем оговорку. Чтобы изобразить саму букву \ следует использовать '\\'
putchar('\'); или printf ("\"); ошибочны.
Надо: putchar('\\'); printf("\\");
Дело в том, что символ \ начинает последовательность из ДВУХ букв, изображающих ОДНУ букву, иногда вызывающую специальные действия на экране или на принтере. */
/* Число делится на n, если ОСТАТОК от деления его на n равен 0, то есть если
(x % n) == 0
В частности, так можно проверять числа на четность/нечетность, беря x%2.
Остатки от деления числа x на n это 0 1 2... n-1. В случае деления на 2 остаток 0 соответствует четному x 1 соответствует нечетному x
*/
/* Задача: Нарисовать треугольник из звездочек в нечетных строках из плюсиков в четных строках *--------------------------------------------------------*
Решение: используем прежнюю программу, добавив в функцию drawOneLine еще один аргумент - symbol - каким символом рисовать строку.
Далее в основном цикле используем условный оператор и проверку номера строки на четность.
*/
#include
void drawOneLine(int nspaces, int nsymbols, char symbol){ int i; /* счетчик */
for(i=0; i < nspaces; i++) putchar(' '); for(i=0; i < nsymbols; i++) putchar(symbol); putchar('\n'); }
/* Мы вынесем объявление этой переменной из функции, сделав ее "глобальной", то есть видимой во ВСЕХ функциях. */ int LINES = 25; /* всего строк. */
void main(){ /* ОБЪЯВЛЕНИЕ ПЕРЕМЕННЫХ */ int nline; /* номер строки */
/* ВЫПОЛНЯЕМЫЕ ОПЕРАТОРЫ (ДЕЙСТВИЯ) */ for(nline=0; nline < LINES; nline++){
if((nline % 2) == 0) /* четное? */ drawOneLine(LINES - nline - 1, nline*2 + 1, '+'); else drawOneLine(LINES - nline - 1, nline*2 + 1, '*'); } } C
/* То же самое, но теперь нужно еще и печатать номер строки. */
#include
/* Вообще-то глобальные переменные принято объявлять в самом начале файла с программой. */
int LINES = 25; /* всего строк. */
/* Добавим к функции еще один аргумент, указатель - печатать ли номер строки. Назовем его drawLineNumber. Не впадите в заблуждение по аналогии с именем ФУНКЦИИ drawOneLine()! В данном случае - это имя ПЕРЕМЕННОЙ - АРГУМЕНТА ФУНКЦИИ.
Оператор if(x).....; РАБОТАЕТ ТАКИМ ОБРАЗОМ (так он устроен): в качестве условия он принимает целое число (типа int). Условие истинно, если x!= 0, и ложно, если x == 0.
Второй добавленный аргумент - собственно номер строки. */ void drawOneLine(int nspaces, int nsymbols, char symbol, /* а это мы добавили */ int drawLineNumber, int linenum ){ int i; /* счетчик */
if(drawLineNumber) printf("%d\t", linenum); /* без перевода строки */
/* На самом деле это условие более полно надо записывать как
if(drawLineNumber!= 0)
но в языке Си это то же самое. */
/* Тут мы снова видим новый специальный символ \t - ТАБУЛЯЦИЯ. Весь экран (или лист бумаги) условно поделен на колонки шириной по 8 позиций. Примерно так: | | | | | | | | |... Символ табуляции вызывает переход из текущей позиции в начало следующей колонки. Например | | | | | | | | |... ^ отсюда
| | | | | | | | |... ^ в это место
*/ for(i=0; i < nspaces; i++) putchar(' '); for(i=0; i < nsymbols; i++) putchar(symbol); putchar('\n'); }
void main(){ /* ОБЪЯВЛЕНИЕ ПЕРЕМЕННЫХ */ int nline; /* номер строки */
/* ВЫПОЛНЯЕМЫЕ ОПЕРАТОРЫ (ДЕЙСТВИЯ) */ for(nline=0; nline < LINES; nline++){
if((nline % 2) == 0) /* четное? */ drawOneLine(LINES - nline - 1, nline*2 + 1, '+', 1, nline); else drawOneLine(LINES - nline - 1, nline*2 + 1, '*', 9, nline); }
/* А почему именно 1 или именно 9? * А все что попало, лишь бы не 0. * Можно 3, 333, 666, -13445, итп * * Вопрос: что будет, если тут написать 0? */ } C
/* Следующая задача будет касаться того, чтобы каждая строка треугольника печаталась в виде: *+*+*+*.....*+*
Тут нам уже придется модифицировать функцию рисования строки. */
#include
int LINES = 25; /* всего строк. */
void drawOneLine(int nspaces, int nsymbols){ int i;
for(i=0; i < nspaces; i++) putchar(' ');
/* в цикле мы будем проверять на четность НОМЕР печатаемого символа. */ for(i=0; i < nsymbols; i++){ if((i % 2) == 0) putchar('*'); else putchar('+'); } putchar('\n'); }
void main(){ int nline; /* номер строки */
for(nline=0; nline < LINES; nline++) { drawOneLine(LINES - nline - 1, nline*2 + 1); } } C
/* Задача нарисовать РОМБ: * *** ***** *** * */
#include
int LINES = 10; /* всего строк в половине ромба. */
void drawOneLine(int nspaces, int nsymbols){ int i;
for(i=0; i < nspaces; i++) putchar(' ');
for(i=0; i < nsymbols; i++) putchar('+'); putchar('\n'); }
void main(){ int nline; /* номер строки */
for(nline=0; nline < LINES; nline++) drawOneLine(LINES - nline - 1, nline*2 + 1);
/* Мы нарисовали треугольник. Теперь нам нужен перевернутый треугольник. Пишем цикл по убыванию индекса. С данного места номера строк отсчитываются в обратном порядке: от LINES-2 до 0 */
for(nline=LINES-2; nline >= 0; nline--) drawOneLine(LINES - nline - 1, nline*2 + 1); } C
/* А теперь рисуем ромб, используя математические формулы. */
#include
void draw(int nspaces, int nstars, char symbol){ int i;
for(i=0; i < nspaces; i++) putchar(' '); for(i=0; i < nstars; i++) putchar(symbol); putchar('\n'); }
void main(){ int LINES = 21; int MIDDLELINE = LINES/2 + 1; /* середина ромба */ int nline;
for(nline=0; nline < MIDDLELINE; nline++) draw(MIDDLELINE - nline -1, nline*2+1, 'A');
/* У следующего цикла for() нет инициализации начального значения индекса. Начальное nline наследуется из предыдущего цикла, таким, каким оно осталось после его окончания, то есть равным MIDDLELINE. */
for(; nline < LINES; nline++) draw(nline - MIDDLELINE + 1, (LINES - 1 - nline) * 2 + 1, 'V'); } * 12_ARRAYS.txt *
|