Условные операторы языка С if и switch .
Для того чтобы иметь возможность реализовать логику в программе используются условные операторы, в которых программа делает выбор по какому из возможных направлений двигаться дальше.
Синтаксис оператора if :
If (выражение)
<оператор>
Если значение параметра «выражение» равно «истинно», выполняется оператор, иначе он пропускается программой. Следует отметить, что «выражение» является условным выражением, в котором выполняется проверка некоторого условия. В таблице 2 представлены варианты простых логических выражений оператора if:
Таблица 2. Простые логические выражения.
if(a < b) | Истинно, если переменная a меньше переменной b и ложно в противном случае. |
if(a > b) | Истинно, если переменная a больше переменной b и ложно в противном случае. |
if(a == b) | Истинно, если переменная a равна переменной b и ложно в противном случае. |
if(a <= b) | Истинно, если переменная a меньше либо равна переменной b и ложно в противном случае. |
if(a >= b) | Истинно, если переменная a больше либо равна переменной b и ложно в противном случае. |
if(a != b) | Истинно, если переменная а не равна переменной b и ложно в противном случае. |
if(a) | Истинно, если переменная а не равна нулю, и ложно в противном случае. |
Пример:
Программа определения знака введенного числа.
#include <stdio.h>
Int main()
{ float x;
printf("Введите число: ");
scanf("%f",&x);
if(x < 0)
printf("Введенное число %f является отрицательным.\n", x);
if(x >= 0)
printf("Введенное число %f является неотрицательным.\n", x);
return 0;
}
Анализ приведенного текста программы показывает, что два условных оператора можно заменить одним, используя конструкцию
If (выражение)
<оператор1>
Else
<оператор2>
которая интерпретируется таким образом. Если «выражение» истинно, то выполняется «оператор1», иначе выполняется «оператор2».
В случаях, когда при выполнении какого-либо условия необходимо записать более одного оператора, необходимо использовать фигурные скобки, т.е. использовать конструкцию вида
If (выражение)
{ <список операторов>}
Else
{ <список операторов>}
Следует отметить, что после ключевого слова else формально можно поставить еще один оператор условия if, в результате получим еще более гибкую конструкцию условных переходов:
if(выражение1) <оператор1>
else if(выражение2) <оператор2>
else <оператор3>
До сих пор рассматривались простые условия типа x<0. Вместе с тем оператор if позволяет реализовывать более сложные условные переходы.
В языке С имеются три логические операции:
&& - логическое И || - логическое ИЛИ ! - логическое НЕТ
На основе этих трех логических операций можно сформировать более сложные условия.
Например, если имеются три переменные exp1, exp2 и exp3, то они могут составлять логические конструкции, представленные в таблице 3:
Таблица 3 . Пример составных логических конструкций
if(exp1 > exp2 && exp2 < exp3 ) | Истинно, если значение переменной expl больше значения переменной exp2 и значение переменной exp2 меньше значения переменной exp3. |
if(exp1 <= exp2 || expl >= exp3 ) | Истинно, если значение переменной exp1 меньше либо равно значения переменной exp2 или значение переменной exp2 больше либо равно значения переменной exp3. |
if(exp1 && exp2 && !exp3) | Истинно, если истинное значение exp1 и истинно значение exp2 и ложно значение exp3. |
if(!exp1 || !exp2 && exp3) | Истинно, если ложно значение exp1 или ложно значение exp2 и истинно значение exp3. |
Подобно операциям умножения и сложения в математике, логические операции И ИЛИ НЕТ, также имеют свои приоритеты. Самый высокий приоритет имеет операция НЕТ, т.е. такая операция выполняется в первую очередь. Более низкий приоритет у операции И, и наконец самый малый приоритет у операции ИЛИ.
Условная операция if облегчает написание программ, в которых необходимо производить выбор между небольшим числом возможных вариантов. Однако иногда в программе необходимо осуществить выбор одного варианта из множества возможных. Формально для этого можно воспользоваться конструкцией if else if ... else.
Однако во многих случаях оказывается более удобным применять оператор switch языка С.
Синтаксис данного оператора следующий:
Switch(переменная)
{ case константа1:
<операторы>
case константа2:
<операторы>
default:
<операторы> }
Данный оператор последовательно проверяет на равенство переменной константам, стоящим после ключевого слова case. Если ни одна из констант не равна значению переменно, то выполняются операторы, находящиеся после слова default. Оператор switch имеет следующую особенность. Допустим, значение переменной равно значению константы 1 и выполняются операторы, стоящие после первого ключевого слова case. После этого выполнение программы продолжится проверкой переменной на равенство константы 2, что часто приводит к неоправданным затратам ресурсов. Во избежание такой ситуации следует использовать оператор break для перехода программы к следующему оператору после switch.
Пример:использования оператора switch.
#include <stdio.h>
Int main()
{ int x;
printf("Введите число: ");
scanf("%d",&x);
Switch(x)
{ case 1 : printf("Введено число 1\n"),break;
case 2 : printf("Введено число 2\n"); break;
default : printf("Введено другое число\п"); }
char ch;
printf("Введите символ: ");
scanf("%с",&сh; switch(ch)
{case 'a': printf("Введен символ а\n"); break;
case 'b': printf("Введен символ b\n"); break;
default : printf("Введен другой символ\n");
}
return 0;
}
Данный пример демонстрирует два разных варианта использования оператора switch. В первом случае выполняется анализ введенной цифры, во втором - анализ введенного символа. Следует отметить, что данный оператор может производить выбор только на основании равенства своего аргумента одному из перечисленных значений case, т.е. проверка выражений типа x<0 в данном случае невозможна.
Операторы циклов языка С.
Часто требуется много раз выполнить одну и ту же группу операторов. Например, для вычисления суммы ряда длиной N или перебора элементов массива с целью определения наибольшего или наименьшего значения и т.п. Во всех этих случаях необходим инструмент для реализации повторяющихся операций и таким инструментом являются операторы цикла.
С помощью оператора while реализуется цикл, который выполняется до тех пор, пока истинно условие цикла. Синтаксис данного оператора следующий:
while(<условие>)
{<тело цикла> }
Пример цикла, в котором выполняется суммирование элементов ряда S = ∑ i , пока S < N:
int N=20, i = 0;
long S = 0;
while(S < N)
{ S=S+i;
i + +; }
В данном примере реализуется цикл while с условием i < N. Так как начальное значение переменной i=0,аN=20, то условие истинно и выполняется тело цикла, в котором осуществляется суммирование переменной i и увеличение ее на 1. Очевидно, что на 20 итерации значение i=20, условие станет ложным и цикл будет завершен.
Продемонстрируем гибкость языка С, изменив данный пример следующим образом:
int N=20, i = 0;
long S = 0;
while((S=S+i++) < N);
В данном случае при проверке условия сначала выполняются операторы, стоящие в скобках, где и осуществляется суммирование элементов ряда и только, затем, проверяется условие. Результат выполнения обоих вариантов программ одинаковый и S=21.
Однако последняя конструкция бывает удобной при реализации опроса клавиатуры, например, с помощью функции scanf():
int num;
while(scanf("%d",&num) == 1)
{ printf("Bы ввели значение %d\n",num); }
Данный цикл будет работать, пока пользователь вводит целочисленные значения и останавливается, если введена буква или вещественное число.
Следует отметить, что цикл whileможно принудительно завершить даже при истинном условии цикла. Это достигается путем использования оператора break. Перепишем предыдущий пример так, чтобы цикл завершался, если пользователь введет число 0.
int num;
while(scanf("%d",&num) == 1)
{ if(num == 0) break;
printf("Вы ввели значение %d\n",num);}
Цикл завершается сразу после использования оператора break, т.е. в приведенном примере, при вводе с клавиатуры нуля функция printf() выполняться не будет и программа перейдет на следующий оператор после while.
Того же результата можно добиться, если использовать составное условие в цикле:
int num;
while(scanf("%d",&num) == 1 && num != 0)
{ printf("Bы ввели значение %d\n",num);}
Таким образом, в качестве условия возможны такие же конструкции, что и в операторе if.
Работа оператора цикла for подобна оператору while с той лишь разницей, что оператор for подразумевает изменение значения некоторой переменной и проверки ее на истинность. Работа данного оператора продолжается до тех пор, пока истинно условие цикла.
Синтаксис оператора for следующий:
for (<инициализация счетчика>;<условие>;<изменение значения счетчика>)
{ <тело цикла> }
Рассмотрим особенность реализации данного оператора на примере вывода таблицы кодов ASCII символов.
char ch;
for(ch = 'a'; ch <= 'z'; ch++)
printf("Значение ASCII для %c - %d.\n",ch,ch);
В данном примере в качестве счетчика цикла выступает переменная ch, которая инициализируется символом ‘a’. Это означает, что в переменную ch заносится число 97 - код символа ‘a’. Именно так символы представляются в памяти компьютера. Код символа ‘z’ - 122, и все малые буквы латинского алфавита имеют коды в диапазоне [97; 122]. Поэтому, увеличивая значение ch на единицу, получаем код следующей буквы, которая выводится с помощью функции printf(). Учитывая все вышесказанное, этот же пример можно записать следующим образом:
for(char ch = 97; ch <= 122; ch++)
printf("Значение ASCII для %c - %d.\n",ch,ch);
Здесь следует отметить, что переменная ch объявлена внутри оператора for. Это особенность языка С - возможность объявлять переменные в любом месте программы.
Существует много особенностей реализации данного оператора, отметим основные из них, которые могут заметно повысить скорость написания программ. Следующим примером продемонстрируем особенности изменения значения счетчика цикла.
int line_cnt = 1;
double debet;
for(debet = 100.0; debet<150.0; debet=debet*1.1, line_cnt++)
printf("%d. Ваш долг теперь равен %.2f.\n", line_cnt, debet);
Следующий фрагмент программы демонстрирует возможность программирования сложного условия внутри цикла.
int exit = 1;
for(int num = 0;num < 100 && !exit; num += 1)
{ scanf("%d",&mov);
if(mov == 0) exit = 0;
printf("Произведение num*mov = %d.\n",num*mov);
}
Оператор for с одним условием:
int i=0;
for( ; i < 100 ; )
i++;
и без условия
int i=0;
For( ; ; ; )
{i++;
if(i > 100) break;
}
В последнем примере оператор break служит для выхода из цикла for, т.к. он будет работать «вечно» не имея никаких условий.
Бывают ситуации, когда целесообразно выполнять проверку условия после того, как будут выполнены операторы, стоящие внутри цикла. Это достигается путем использования операторов do while, которые реализуют цикл с постусловием.
Пример:
const int secret_code = 13;
int code_ent;
do {
printf("Введите секретный код: ");
scanf("%d",&code_ent);
}
while (code_ent != secret_code);
Цикл с постусловием работает до тех пор, пока истинно условие, то есть пока значение введенного кода будет отличаться от значения секретного кода.
Следует обратить внимание на то, что после ключевого слова while должна стоять точка с запятой. При реализации данного цикла можно использовать составные условия, подобно циклу while, а также принудительно выходить из цикла с помощью оператора break.
Все рассмотренные выше операторы циклов допускают использование любых других операторов языка С внутри цикла, в том числе и операторов цикла. Это значит, что внутри одного цикла может находиться другой, что приводит к реализации вложенных циклов. Вложенные циклы необходимы для решения большого числа задач, например, вычисления двойных, тройных и т.д. сумм, просмотр элементов двумерного массива и многих других задач. В качестве примера вложенных циклов рассмотрим задачу вычисления суммы
двойного ряда S = ∑∑(i* j):
long S = 0;
int M = 10, N = 5;
for( int i = 0; i <= N; i++)
{ for( int j = 0; j <= M; j++)
S += i*j; }
Того же результата можно добиться и с помощью оператора цикла while.
Массивы.
Представление данных в виде отдельных переменных не всегда достаточно при программировании реальных задач. Например, для представления поведения сигнала во времени или хранения информации об изображении удобно использовать специальный тип данных - массивы. Одномерные массивы можно ассоциировать с компонентами вектора, а двумерные - с матрицами. В общем случае массив - это набор элементов данных одного типа, для объявления которого используется следующий синтаксис:
<тип данных> <имя массива>[число элементов];
Например:
int array_int[100]; //одномерный массив 100 целочисленных
// элементов
double array_d[25]; //одномерный массив 25 вещественных
// элементов
Объявление массивов отличается от объявления обычных переменных наличием квадратных скобок [ ].
Также имена массивов выбираются по тем же правилам, что и имена переменных. Обращение к отдельному элементу массива осуществляется по номеру его индекса.
Пример: запись в массив значений линейной функции
f (х) = kx + b и вывода значений на экран:
double k=0.5, b = 10.0;
double f[100];
for(int x=0; i < 100; i++)
{ f[i] = k*x+b;
printf("%.2f ",f[i]); }
В языке С предусмотрена возможность инициализации массива в момент его объявления, например, таким образом
int powers[4] = {1, 2, 4, 6};
В этом случае элементу powers[0] присваивается значение 1, powers[1] - 2, и т.д.
Особенностью инициализации массивов является то, что их размер можно задавать только константами, а не переменными.
Например, следующая программа приведет к ошибке при компиляции:
int N=100;
float array_f[N]; //ошибка, так нельзя.
Поэтому при объявлении массивов обычно используют такой подход:
#include <stdio.h>
#define N 100
Int main()
{ float array_f[N];
return 0; }
Следует отметить, что при инициализации массивов число их элементов должно совпадать с его размерностью.
Рассмотрим вариант, когда число элементов при инициализации будет меньше размерности массива.
#define SIZE 4
int data[SIZE]={512, 1024};
for(int i = 0; i < SIZE; i++)
printf("%d, \n",data[i]);
Результат работы программы будет следующим:
512, 1024, 0, 0
Из полученного результата видно, что неинициализированные элементы массива принимаются равными нулю. В случаях, когда число элементов при инициализации превышает размерность массива, то при компиляции произойдет ошибка. Поэтому, когда наперед неизвестно число элементов, целесообразно использовать такую конструкцию языка C++:
int data[] = {2, 16, 32, 64, 128, 256};
В результате инициализируется одномерный массив размерностью 6 элементов.
Если значение индекса при обращении к элементу массива превысит его размерность, то этом случае ни программа, ни компилятор не выдадут значение об ошибке, но в программе могут возникать непредвиденные ошибки.
Поэтому программисту следует обращать особое внимание на индексы, которые при обращении к элементам массива не выходили за его пределы.
Также следует отметить, что первый элемент массива всегда имеет индекс 0, второй - 1, и т.д.
Для хранения некоторых видов информации, например, изображений удобно пользоваться двумерными массивами. Объявление двумерных массивов осуществляется следующим образом:
int array2D[100][20]; //двумерный массив 100x20 элементов
Нумерация элементов также начинается с нуля, т.е. array2D[0][0] соответствует первому элементу, array2D[0][1] - элементу первой строки, второго столбца и т.д.
Для начальной инициализации двумерного массива может использоваться следующая конструкция:
long array2D[3][2] = {{1, 2},{3, 4},{5, 6}};
или
long array2D[][] = {{1, 2},{3, 4},{5, 6}};
В общем случае можно задать массив любой размерности и правила работы с ними аналогичны правилам работы с одномерными и двумерными массивами.
Работа со строками в языке С .
В языке С нет специального типа данных для строковых переменных. Для этих целей используются массивы символов (тип char).
Пример:
char str_1[100] = {'П,,,р,,,и,,,в,,,е,,,т,,,\0'};
char str_2[100] = "Привет";
char str_3[ ] = "Привет";
printf("%s\n%s\n%s\n",str_1,str_2,str_3);
В приведенном примере показаны три способа инициализации строковых переменных.
Первый способ является классическим объявлением массива,
второй и третий используются специально для строк. Причем в последнем случае, компилятор сам определяет нужную длину массива для записи строки.
Cпециальные управляющие коды, которые показывают, где заканчивается строка или где используется перенос внутри одной строки и т.п.
В частности символ ‘\0’ означает в языке С++ конец строки и все символы после него игнорируются как символы строки.
Пример :
char str1[10] = {'Л','е','к','ц','и','я','\0'};
char str2[10] = {'Л','е','к','ц', '\0','и','я'};
char str3[10] = {'Л','е', '\0','к','ц','и','я'};
printf("%s\n%s\n%s\n",str1,str2,str3);
Результатом работы данного кода будет вывод следующих трех строк:
Лекция
Лекц
Ле
Из этого примера видно как символ конца строки ‘\0’ влияет на длину строк. Таким образом, чтобы подсчитать длину строки (число символов) необходимо считать символы до тех пор, пока не встретится символ ‘\0’ или не будет достигнут конец массива. Функция вычисления размера строк уже реализована в стандартной библиотеке языка С string.h и имеет следующий синтаксис:
int strlen(char* str);
где char* str - указатель на строку.
Пример использования функции strlen():
#include <stdio.h>
#include <string.h>
int main(void)
{ char str[ ] = "Привет мир!";
int length = strlen(str);
printf("Длина строки = %d.\n",length);
return 0; }
Результатом работы программы будет вывод на экран числа 11. Учитывая, что первый символ имеет нулевой индекс, то можно заметить, что данная функция считает и символ ‘\0’.
Пусть объявлены две строки
char str1[ ] = "Это первая строка";
char str2[ ] = "Это вторая строка";
и необходимо выполнить оператор присваивания
str1 = str2;
При такой записи оператора присваивания компилятор выдаст сообщение об ошибке.
Для того чтобы выполнить копирование необходимо перебирать по порядку элементы одного массива и присваивать их другому массиву. Данная функция реализована в библиотеке языка С string.h и имеет следующее определение:
char* strcpy(char* dest, char* src);
Она выполняет копирование строки src в строку dest и возвращает строку dest.
Пример использования функции strcpy():
#include <stdio.h>
#include <string.h>
int main(void)
{
char src[] = "Привет мир!";
char dest[100];
strcpy(dest,src);
printf("%s\n",dest);
return 0; }
Кроме операций вычисления длины строки и копирования строк важной является операция сравнения двух строк между собой.
В языке С две строки считаются одинаковыми, если равны их длины и элементы одной строки равны соответствующим элементам другой. Функция сравнения двух строк имеет вид:
int strcmp(char* str1, char* str2);
и реализована в библиотеке string.h. Данная функция возвращает нуль, если строки str1 и str2 равны и не нуль в противном случае.
Пример использования данной функции:
char str1[ ] = "Это первая строка";
char str2[ ] = "Это вторая строка";
if(strcmp(str1,str2) ==0)
printf("Строка %s равна строке %s\n",str1,str2);
else printf("Строка %s не равна строке %s\n",str1,str2);
В языке С имеется несколько функций, позволяющих вводить строки с клавиатуры. Самой распространенной из них является ранее рассмотренная функция scanf(), которой в качестве параметра передается ссылка на массив символов:
char str[100];
scanf("%s",str);
В результате выполнения этого кода, переменная str будет содержать введенную пользователем последовательность символов. Кроме функции scanf() также часто используют функцию gets() библиотеки stdio.h, которая в качестве аргумента принимает ссылку на массив символов:
gets(str);
Данная функция считывает символы до тех пор, пока пользователь не нажмет клавишу Enter, т.е. введет символ перевода строки ‘\n’.
Затем она записывает вместо символа ‘\n’ символ ‘\0’ и передает строку вызывающей программе.
Для вывода строк на экран помимо функции printf() можно использовать также функцию puts() библиотеки stdio.h, которая более проста в использовании. Пример:
#define DEF "Заданная строка"
char str[ ] = "Это первая строка";
puts(str);
puts (DEF);
puts(&str[4]);
Результат работы следующий: