Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

37.ИНФОРМАТИКА Book си

.pdf
Скачиваний:
37
Добавлен:
23.03.2016
Размер:
1.14 Mб
Скачать

Часть 1. Язык Си

21

int a,b,max;

if (a>b) max=a; else max=b;

Обратите внимание, что оператор перед else оканчивается точкой с запятой. А если мы захотим здесь, чтобы результат еще и выводился на экран, то конструкция будет выглядеть следующим образом:

if (a>b) { max=a; printf("а>b max=a=%d",max); } else { max=b; printf("а<=b max=b=%d",max); }

Здесь стоит обратить внимание на расстановку точек с запятой и фигурных скобок блоков. Условный оператор здесь размещен на двух строках, однако его можно написать как на одной, так и на большем количестве строк (см. Синтаксис языка Си). Подробно о работе функции printf можно узнать из приложения.

В языке Си оператор else ассоциируется с ближайшим if в соответствующем блоке. Часто встречается необходимость использовать следующую конструкцию:

if (<условие 1>) <оператор 1>;

else if (<условие 2>) <оператор 2>;

. . .

else if (<условие n>) <оператор n>; else <оператор ‖кроме‖>;

В этой форме условия операторов if проверяются сверху вниз. Как только какое-либо из условий принимает значение ‖истина‖, выполняется оператор, следующий за этим условием, а вся остальная часть конструкции игнорируется.

Программа для вычисления корней квадратного уравнения

Прежде чем начать писать программу, разберем, что требуется сделать и как это осуществить. Квадратное уравнение имеет следующий вид: ax²+bx+c=0, где a, b и с — константы. С клавиатуры вводятся a, b и с; результатом работы программы является вывод на экран всех корней квадратного уравнения, если они есть, или сообщения об их отсутствии, если их нет.

Если уравнение действительно является квадратным (a≠0), то вычисляем дискриминант. Если он больше нуля, то уравнение имеет два корня, если равен нулю, то один, а если меньше нуля, то корней нет. Однако могут встречаться и вырожденные случаи, когда a=0. Тогда уравнение имеет вид: bx+c=0. Отсюда видно, что при b≠0 есть

21

22

Си и Си++

 

 

один корень, а при b=0 может быть два случая: когда корней нет (c≠0) и когда любое x является корнем (c=0). В результате получаем шесть вариантов решения. Программа, написанная для всех шести случаев, будет корректно работать при любых введенных значениях коэффициентов. Например, эта программа может выглядеть так:

#include<stdio.h> /* библиотека ввода-вывода */ #include<math.h> /* математическая библиотека */ #include<conio.h> /* библиотека ввода-вывода */

void main() {

 

 

float a,b,c,d;

 

 

clrscr();

/* очистка экрана */

printf("a="); scanf("%f",&a);

/* ввод a */

printf("b="); scanf("%f",&b);

/* ввод b */

printf("c="); scanf("%f",&c);

/* ввод c */

if (a) {

/* или if (a!=0) */

d=b*b-4*a*c;

/* Дискриминант */

if (d<0) printf("Решений нет\n");

else if (d==0) printf("x=%f\n",b/(2*a));

else {

/* !(d<=0) <=> d>0 */

float x1,x2; /* переменные блока */ x1=(-b-sqrt(d))/(2*a); x2=(-b+sqrt(d))/(2*a); printf("x1=%f , x2=%f\n",x1,x2);

}

} else {

if (b) printf("x=%f\n",-c/b);

/* или if (b!=0) */ else if (c) printf("Решений нет\n");

else printf("Любое x\n");

}

getch(); /* Ожидание нажатия клавиши */

}

Разберем эту программу. Библиотека stdio.h подключается для работы с функциями scanf и printf; math.h — для функции вычисления корня sqrt; conio.h — для функции очистки экрана clrscr и функции получения символа с клавиатуры getch. Подробно о работе этих функций можно прочитать в приложении. После

22

Часть 1. Язык Си

23

 

 

имени функции всегда ставятся круглые скобки, даже если не передаются параметры.

Тип void перед main говорит о том, что функция не возвращает значений. Если не поставить void, то компилятор будет выдавать предупреждение, что функция main не возвращает значения.

В данной программе введен дополнительный блок при d>0, который показывает возможности объявления переменных. Переменные x1 и x2 являются локальными и действуют только в данном блоке, т.е. до закрывающей фигурной скобки. Можно было обойтись без этого блока, написав вместо него следующий оператор:

printf("x1=%f , x2=%f\n", (-b-sqrt(d))/(2*a),(-b+sqrt(d))/(2*a));

Оператор switch

Язык Си имеет встроенный оператор множественного выбора switch. Он похож на оператор case в Паскале. Основная форма оператора имеет такой вид:

switch (<выражение>) { case <значение 1>:

<последовательность операторов> [break;]

. . .

case <значение n>: <последовательность операторов> [break;]

[ default:

<последовательность операторов> ]

}

Сначала вычисляется выражение в скобках за ключевым словом switch. Затем просматривается список меток (case <значение 1> и т. д.) до тех пор, пока не находится метка, соответствующая значению вычисленного выражения. Далее происходит выполнение соответствующей последовательности операторов, следующей за двоеточием. Если же значение выражения не соответствует ни одной из меток оператора switch, то выполняется последовательность операторов, следующая за ключевым словом default. Допускается конструкция оператора switch, когда слово default и соответствующая

23

24

Си и Си++

 

 

последовательность операторов могут отсутствовать. Заметим, что здесь речь идет не об операторе, а именно о последовательности операторов, поэтому если требуется выполнить несколько операторов, то здесь не надо из них делать блок.

Когда после ―последовательности операторов‖ встречается оператор break, то его выполнение приводит к выходу из оператора switch и переходу к следующему оператору программы. Наличие оператора break в операторе switch необязательно. Однако если оператор break отсутствует, то выполнение продолжается до первого оператора break или до конца оператора switch, т.е. невзирая на то, что операторы могут относиться к другому case.

Пример 1:

 

Пример 2:

int a=1;

 

int a=1;

switch (a) {

 

switch (a) {

case 0:

 

case 0:

printf("0 ");

break;

printf("0 ");

case 1:

 

case 1:

printf("1 ");

break;

printf("1 ");

case 2:

 

case 2:

printf("2 ");

break;

printf("2 ");

default:

 

default:

printf("a>2 ");

 

printf("a>2 ");

}

 

}

На экране:

 

 

1

 

1 2 a>2

Циклы while и do while

Оператор цикла с предусловием в языке Си имеет следующий вид:

while (<условие>) <оператор>

―Условие‖, как и во всех других операторах, является просто выражением. Цикл выполняется до тех пор, пока условие принимает значение ―истина‖. Когда же условие примет значение ―ложь‖, программа передаст управление следующему оператору программы. В цикле while сначала проверяется условие, а затем выполняется оператор. Данный цикл работает аналогично циклу while do в Паскале.

В Си также есть оператор цикла с постусловием. Основная его

24

Часть 1. Язык Си

25

 

 

форма следующая:

do <оператор> while (<условие>);

Какое бы ―условие‖ ни стояло, оператор выполнится хотя бы один раз (в цикле while оператор может не выполниться ни разу). ―Оператор‖ выполняется, пока ―условие‖ является ―истинным‖ (отличным от нуля). Заметим, что оператор чаще всего помещается в фигурные скобки, даже если он в цикле один. Это повышает читаемость программы, а также для того, чтобы не спутать (программисту, а не компилятору) с оператором while. Аналогичным оператором цикла в Паскале является repeat until. Отличие заключается в том, что в Паскале ―условие‖ является условием выхода из цикла, а в Си ―условие‖ является условием выполнения цикла.

Оператор do while удобно использовать для корректного ввода. Далее приведен фрагмент программы для ввода положительного числа a:

int a; do {

printf("\nВведите a>0. a:"); scanf("%d",&a);

} while (a<=0);

Цикл for

Форма цикла for имеет следующий вид:

for (<нач. знач.> ; <условие> ; <изменение>) <оператор>

В простейшей форме ―нач. знач.‖ используется для присвоения начального значения параметру цикла. ―Условие‖ — обычно условное выражение, которое определяет условие выполнения цикла. ―Изменение‖ обычно используется для приращения параметра цикла на каждом шаге. Эти три раздела заголовка цикла должны быть разделены точкой с запятой. Выполнение цикла происходит до тех пор, пока условное выражение истинно. Простейший пример оператора цикла for:

for(i=0; i<10; i++) printf("%d\n", i);

В результате выполнения этого оператора будут напечатаны в столбик цифры от 0 до 9. Для печати этих цифр в обратном порядке можно использовать следующий оператор:

25

26

Си и Си++

 

 

for(i=9; i>=0; i--) printf("%d\n", i);

Цикл for похож на аналогичные циклы в других языках программирования, и в то же время этот оператор в языке Си гораздо более гибкий, мощный и применим во многих ситуациях. В качестве параметра цикла необязательно использовать целочисленный счетчик. Приведем фрагмент программы, выводящей на экран буквы английского алфавита:

unsigned char ch;

for (ch='a'; сh<='z'; ch++) printf("%c ", ch);

Следующий фрагмент программы

for(ch='-'; ch!='N';) scanf("%c", &ch);

будет выполняться до тех пор, пока с клавиатуры не будет введен символ 'N'. Заметим, что место, где должно быть приращение, пусто. Случайно или намеренно может получиться цикл, из которого нет выхода, так называемый бесконечный цикл.

Приведем два примера таких циклов.

for (;;) рrintf("Бесконечный цикл\n");

for (i=10;i>6;i++) printf("Бесконечный цикл\n");

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

Операторы break и continue

Оператор break имеет два применения. Первое — окончание case в операторе switch. Второе — немедленное окончание цикла, не связанное с проверкой обычного условия окончания цикла. Когда оператор break встречается внутри оператора цикла, то происходит немедленный выход из цикла и переход к выполнению оператора, следующего за оператором цикла.

Еще один полезный оператор — оператор continue. Если он встретился в операторе цикла, то происходит передача управления на начало следующей итерации цикла. В циклах while и do while — на проверку условия, в цикле for — на приращение. Этот оператор необходим, если вы хотите закончить текущую итерацию цикла и не

26

Часть 1. Язык Си

27

 

 

выполнять оставшиеся операторы, а сразу перейти к следующей итерации цикла.

Например, эти операторы можно использовать в программе, угадывающей случайным образом загаданное число.

#include<stdio.h>

#include<conio.h>

#include<stdlib.h> /* для функции random */ void main() {

int x,y,i,s; char c; clrscr();

randomize(); /* инициализация random */ x=random(10);

for (i=1, s=0;;i++) { y=random(10);

if (x==y) { s++;

printf("\nЧисло %d угадано.\n"); printf("Закончить (y/n)?\n",x); do { c=getch();

}while (c!='y' && c!='Y'

&&c!='n' && c!='N'); if (c=='y' || c=='Y') break; x=random(10);

continue;

}

printf("%d ",y);

}

printf("%d из %d\n",s,i); getch();

}

Однако эту программу можно написать без использования break

и continue:

void main() { int x,y,i,s; char c=' '; clrscr();

27

28

Си и Си++

randomize();

x=random(10);

for (i=1, s=0; c!='y' && c!='Y'; i++) { y=random(10);

if (x==y) { s++;

printf("\nЧисло %d угадано.\n"); printf("Закончить (y/n)?\n",x); do { c=getch();

}while (c!='y' && c!='Y'

&&c!='n' && c!='N'); x=random(10);

/* if (c=='n' || c=='N') можно опустить*/ } else printf("%d ",y);

}

printf("%d из %d\n",s,i); getch();

}

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

Оператор goto

Язык Си обладает всеми возможностями для написания хорошо структурированных программ без использования оператора goto. В структурном программировании считается дурным тоном использование оператора goto. Он запутывает логику, программа теряет наглядность, что часто приводит к написанию логически неверного алгоритма. При этом ошибки очень сложно ―выловить‖. Тем не менее, оператор goto в языке Си есть и иногда он может быть полезен, хотя без него можно обойтись в любой ситуации. Для использования оператора goto надо ввести понятие метки. Метка — это идентификатор, за которым следует двоеточие. Метка должна находиться в той же функции, что и оператор goto. В отличие от Паскаля, метки в языке Си заранее не описываются. Одно из полезных применений оператора goto — это выход из вложенных циклов:

28

Часть 1. Язык Си

29

for (;;) { for(;;) {

. . .

goto ExitFor;

}

}

ExitFor: printf ("Быстрый выход из вложенных циклов");

29

30

Си и Си++

 

 

1.7 Массивы и указатели

Массивы

Основная форма объявления массива размерности N такова:

<тип> <имя массива>[<размер1>][<размер2>][<размер N>];

Чаще всего используются одномерные массивы:

<тип> <имя массива>[<размер>];

―тип‖ — базовый тип элементов массива, ―размер‖ — количество элементов одномерного массива.

Объявление многомерного массива можно трактовать как объявление массива массивов, т.е. массива размера <размер N>, элементами которого являются многомерные массивы

<имя массива>[<размер1>][<размер2>]…[<размер N-1>].

Размер массива может задаваться константой или константным выражением. Нельзя задать массив переменного размера. Для этого существует отдельный механизм, называемый динамическим выделением памяти.

В языке Си индекс всегда начинается с нуля. Когда мы говорим о первом элементе массива, то имеем в виду элемент с индексом 0. Если мы объявили массив int а[100], это значит, что массив содержит 100 элементов от а[0] до а[99]. В языке Си под одномерные массивы всегда выделяется непрерывное место в оперативной памя-

ти. В языке Си не проверяется выход индекса за пределы массива.

Если в программе описан массив а[100], а Вы попытаетесь использовать элемент а[200], то сообщение об ошибке не будет выдано, а

вкачестве значения этого элемента будет выдано некоторое заранее не определенное число (символ).

Для присваивания элементам массива начальных значений следует необходимые значения написать через запятую, взяв все эти значения

вфигурные скобки. Например:

int a[10]={1,2,3,4,5,6,7,8,9,10};

Здесь тоже многомерный массив можно рассматривать как массив массивов. Например, следующие два объявления эквивалентны:

30