Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

Лекция 7: операторы выбора, безусловный переход, циклы.

Лекция 1: введение в программирование. | Лекция 2: представление данных в компьютере. | Часть 1: хранение данных в компьютере. | Часть 2: типы данных языка Си. | Часть 1: основные операторы и их приоритеты. | Часть 2: функции. | Лекция 5: Функция main, функции ввода-вывода, препроцессор. | Лекция 9: связные списки. | Часть 2: бинарные деревья. | Часть 3: динамическое программирование. |


Читайте также:
  1. Professional SPA Collection (Профессиональная СПА Коллекция).
  2. Витэ и жизненные циклы.
  3. Вложенные операторы If. Логические операции и выражения
  4. Вложенные операторы if. Сложное условие в операторе if. Логические операции
  5. Встроенные операторы и их приоритет
  6. Глава 4. Простые (линейные) программы. Операторы ввода-вывода. Переменные величины
  7. Домашние задания по теме: « Фирма в рыночной экономике» (лекция № 2).

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

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

Условный оператор (if) в общем виде выглядит так: if (<логическое выражение>) <оператор1>[else <оператор2>]

Оператор1 выполняется если логическое выражение является истинным, а оператор2 (необязательный) – если ложным. Оба оператора могут быть как одиночными – заканчивающимися точкой с запятой – так и блочными, заключёнными в фигурные скобки. Логическое выражение может быть чем угодно – выражением, переменной, константой – в любом случае, если его значение 0, оно будет считаться ложным, а при любом ненулевом значении – истинным. Например:

if(a==1) a=1;

if(b)

{

c*=5;

b=getchar();

}

if((e=(int*)malloc(50*sizeof(int)))==NULL)

{

printf("Can’t allocate memory\n");

return NULL;

}

else

{

e[7]=10;

e[12]=56;

}

Условный оператор имеет ещё одну форму записи, называемую тернарный условный оператор. В общем виде он выглядит так: <выражение1>?<выражение2>:<выражение3>

Вся эта конструкция равна выражению2, если выражение1 истинно, и выражению3, если ложно.

Другим оператором выбора является переключатель (switch). Он предназначен для выбора из нескольких альтернатив. В общем виде его запись выглядит так:

switch(<выражение>):

{

case <значение1>:

<операторы1>;

break;

case <значение2>:

<операторы2>;

break;

/*и так далее…*/

case <значениеN>:

<операторыN>;

break;

default:

<операторы_default>;

break;

}

В случае если значение выражения равно значению1[25], то выполняются операторы1, если значению2 – то операторы2, и так далее. Если выражение не равно ни одному из приведённых значений, то выполняются операторы необязательного блока default (если его нет, то не выполнятся ничего). О том, что делает оператор break будет сказано чуть позднее, пока нужно просто запомнить, что он стоит в конце каждого блока операторов. Например:

switch (menu_item):

{

case 1:

function1(a, b, c);

break;

case 2:

fucntion2(c, b, e);

printf("%d\n", function3(a, e, c));

break;

case 3:

free(a);

return 0;

default:

printf("Error: incorrect item!\n");

break;

}

Если попытаться представить себе, как работают операторы выбора, то станет ясно, что они в зависимости от условия позволяют процессору «перескакивать» через те или иные участки кода. Существует и оператор, осуществляющий этот перескок безо всяких условий – безусловный переход goto. Его синтаксис в общем виде таков: goto <метка>;. Метка – это собственное имя строки программного кода. Чтобы назначить строке имя нужно написать <имя_метки>:. Например:

i=0;

label:

if(i>n) goto resume;

a[i]=i;

++i;

goto label;

resume:

Нужно всем элементам массива a от 0 до n присвоить значение, равное их порядковому номеру. Для этого нужна дополнительная переменная i, которой будет сначала присвоено значение 0. Потом следует проверка – не превышает ли i значение n. После этого выполняется собственно присваивание значения i-тому элементу массива. Затем значение i увеличивается на 1 и происходит переход обратно на шаг проверки принадлежности i правильному диапазону. Если проверка не пройдена, то происходит переход к дальнейшему выполнению программы.

Видно, что это не слишком легко читаемый код. Если же речь идёт о трёхмерном массиве (встречаются и такие), то код становится совершенно нечитаемым и недопустимо сложным[26]:

k=0;

label1:

if(k>l) goto resume1;

j=0;

label2:

if(j>m) goto resume2;

i=0;

label3:

if(i>n) goto resume3;

a[i, j, k]=i*j*k;

++i;

goto label3;

resume3:

++j;

goto label2;

resume2:

++k;

goto label1;

resume1:

Поэтому применение безусловных переходов противоречит любой парадигме программирования на языках высокого уровня. Во всех императивных языках есть мощное и удобное средство решения подобных задач – циклы. Циклом называется последовательность операторов, выполняемая несколько раз, в зависимости от какого-то условия. Операторы, выполняемые несколько раз, называются телом цикла. Однократное выполнение тела цикла называется итерацией. Циклы делятся на циклы с предусловием и циклы с постусловием. В циклах с предусловием истинность условия выполнения цикла проверяется до начала итерации (как в примерах с goto). В языке Си цикл с предусловием в общем виде выглядит так: while (<выражение>) <оператор>. Цикл выполняется только в том случае, если выражение не равно нулю. Тело цикла составляет оператор, которых может быть как одиночным, так и блочным. Например:

while(i)

{

putchar(c[i]);

--i;

}

Этот цикл выводит на экран строку c задом наперёд, начиная с i-той позиции. Когда значение i становится равным 0, цикл останавливается.

В циклах с постусловием истинность условия выполнения цикла выполняется после завершения итерации. Это значит, что цикл будет выполнен хотя бы один раз, в независимости ни от чего. В общем виде этот цикл выглядит так: do {<тело цикла>}[27] while(<условие>). Например, напишем функцию, которая будет запрашивать ввод числа от 1 до 10[28]:

char inten()

{

char i;

do

{

printf("Enter integer number of 1 to 10: ");

scanf("%d", &i);

if((i<1)||(i>10)) printf("Incorrect number!\n");

}

while((i<1)||(i>10));

return i;

}

Чтобы реализовать такую функцию на цикле с предусловием, пришлось бы тело переписать ещё один раз, перед циклом.

Для управления выполнением цикла есть два оператора: break и continue. Оператор break принудительно прекращает выполнение цикла. Оператор continue прекращает выполнение текущей итерации, переходя к выполнению следующей.

Часто встречаются ситуации, в которых нужен цикл с предусловием, перебирающий все значения какой-то одной переменной в заданном диапазоне. Например, нужно перебрать все элементы массива. Для таких случаев существует удобная форма записи, называемая циклом со счётчиком. Его общий вид таков: for(<оператор1>;<выражение>;<оператор2>) <тело цикла>. Оператор1 выполняется перед первой итерацией цикла (и больше никогда), оператор2 выполняется после каждой итерации (в том числе и завершённой оператором continue). Цикл выполняется пока выражение не равно нулю. В такой форме удобно записывать циклы вида for(<счётчик>=<значение>;<условие на счётчик>;<счётчикn>=f(<счётчикn-1>)) <тело цикла>. В этом случае переменная, называемая счётчиком, инициализируется каким-то значением, затем на неё ставится условие, при невыполнении которого цикл прекратится и задаётся рекуррентное соотношение между значениями счётчика в соседних итерациях. Например:

for(i=0;i<n;i+=2) a[i]=i<<1;

Этот цикл пронумерует все чётные элементы массива, то есть: второй элемент будет иметь значение 1, четвёртый –2, шестой – 3 и так далее. Конечно, между циклами while и for существует взаимнооднозначное соответствие, поэтому никаких особых условий на выражения для for не существует. Не будет ни ошибкой, ни предупреждением использование такой конструкции:

for(i=0;c[i]!=0;++i) putchar(c[i]);

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

В качестве примера напишем функцию, подсчитывающую количество единичных битов в передаваемой ей строке.

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

Как же узнать значение младшего бита? Вспомним, что в двоичном представлении все разряды кроме младшего соответствуют чётным числам. Два чётных числа при сложении дают чётное[29]. Значит, число с нулевым младшим битом обязательно чётное, а с единичным – нечётное. Отсюда следует, что остаток от деления числа на два равен его младшему биту. Надо провести эту операцию для всех бит числа:

s=0;

for(i=0; i<=7; ++i) s+=(chr>>i)%2;

где chr – исследуемый символ, s – количество единичных бит в нём. Чтобы избежать проблем с неправильной интерпретацией значений, обе переменные должны иметь тип unsigned char. Строки обычно имеют тип char, этот случай мы и рассматриваем. Поэтому для каждого элемента строки c[j] нужно произвести присваивание, совершенно неверное с точки зрения преобразования типов, но сохраняющее битовое представление:

chr=c[j];

считая, что c[j] имеет тип сhar, chr – unsigned char. Следует отметить, что сдвиг применяется к переменной chr, а потому сама передаваемая строка не изменится, что и требуется.

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

int bitcount(char* c)

{

unsigned char chr, s;

int i, j, summ=0;

for(j=0; c[j]!=0; ++j)

{

s=0;

chr=c[j];

for(i=0; i<=7; ++i) s+=(chr>>i)%2;

summ+=(int)s;

}

return summ;

}


Дата добавления: 2015-11-13; просмотров: 64 | Нарушение авторских прав


<== предыдущая страница | следующая страница ==>
Лекция 6: массивы и строки, библиотечные функции ввода-вывода.| Лекция 8: структуры и объединения.

mybiblioteka.su - 2015-2024 год. (0.013 сек.)