Конструкции цикла и передачи управления

Операторы цикла задают многократное исполнение операторов тела цикла. Определены три разных оператора цикла:

цикл с предусловием

while (выражение_условие)

тело_цикла;

цикл с постусловием

do

тело_цикла

while (выражение_условие)

цикл for

for (инициализация_цикла; выражение_условие; список_выражений)

тело_цикла

Тело_цикла не может быть описанием, это либо один (может быть и пустой) оператор, который всегда завершается точкой с запятой, либо блок операторов, заключенных в скобки {}. Выражение_условие – это выражение, определяющее условие продолжения итераций. Операторы тела цикла выполняются, пока условие истинно (не равно нулю). Инициализация_цикла в цикле for – это последовательность определений и выражений, разделяемых запятыми. Даже если она пустая, точка с запятой должна присутствовать. Чаще всего здесь устанавливается начальные значения счетчиков и параметров цикла. Выражения из списка_выражений выполняются после выполнения операторов тела цикла и до следующей проверки выражения_условия.

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

for (int i=1,s=0;i<=k; i++) s+=i*i;

for (int i=0,s=0;i<=k; s+=++i*i);// тело цикла – пустой оператор;

for (int i=0,s=0;i<=k;) s+=++i*i; //отсутствует список выражений;

for (int i=0,s=0;i<=k;) {int j; j=++i; s+=j*j;}

Конструкцию for чаще применяют для организации детерминированных циклов, то есть циклов, число повторений которых заранее определено. А конструкции do и while чаще применяют для итерационных циклов, то есть циклов, число повторений которых зависит от выполнения условий, изменяющихся в теле цикла и заранее не определено. Хотя такое функциональное разделение довольно условно.

Каждый цикл может содержать вложенные циклы. Для принудительного выхода из цикла используется оператор break. Необходимость в его использовании возникает, когда условия продолжения итераций надо проверять не только в начале или конце итерации, но и в середине тела цикла. Например, если начальные значения переменных i,j таковы, что i<j, то следующий цикл определяет наименьшее целое, не меньшее их среднего арифметического.

while (i<j)

{

i++;

if (i==j) break;

j––;

}

Для i=0, j=3 результат i=j=2 достигается при выходе из цикла с помощью оператора break, а для i=0, j=2 результат i=j=1 достигается при естественном завершении цикла.

Циклические конструкции чаще всего применяют для ввода значений элементов массива. Массив – это именованная совокупность элементов одного типа, расположенных в памяти компьютера последовательно. Описание массива имеет два формата:

тип идентификатор[размерность];

или

тип идентификатор [размерность]={элемент1, элемент2,…};

Размерность – это константное выражение целого типа, обозначающее количество элементов в массиве. Если в первом случая описания размерность необходимо указывать, то во втором его указание не обязательно. Когда массив объявлен без указания размерности, но при этом инициализирован списком, его размерность вычисляется путем подсчета числа элементов этого списка. Явная инициализация массива разрешена только при его определении и возможна двумя способами: либо с указанием размера массива в квадратных скобках, либо без его явного указания, например,

int а[6]={1,2,3,4};//массив из 6 целых чисел с инициализацией

//первых четырех

char str[]={‘a’, ‘b’, ‘c’};// массив из 3 элементов типа char

Рекомендуется задавать размерность массива, не как числовую, а как именованную константу. Например, запись const int n=5; int a[n]; предпочтительнее записи int a[5];.

Одним из способов доступа к элементам массива является использование индексов. То есть, чтобы обратится к i– тому элементу массива а, используют запись а[i], при этом первый элемент массива имеет индекс 0. Например, ввод элементов массива с клавиатуры можно реализовать следующим образом:

const int n=10;

int a[n];

for (int i=0;i<n;i++)

{

printf("\nЕnter a[%d%s",i, "] ");

scanf ("%d",&a[i]);

}

Если требуется сгенирировать элементы случайным образом, то используют функцию rand() из библиотеки stdlib.h. Данная функция возвращает случайное целое число в диапозоне от 0 до 255. Для того, чтобы изменить диапозон можно вязть остаток от деления результата этой функции на нужное границ диапазаона. Например, сгенирировать элементы массива в диапозоне [0,40] можно следующим образом:

const int n=10;

int a[n];

const int k=40;//граница диапозона [0,40]

for (int i=0;i<n;i++)

a[i]= rand()%(k+1) //генерируем массив случайных чисел

//в диапазоне [0..k]

Если в задачи указано, что размерность массива не превышает, например N=20, то можно задать эту размерность константой, а реальное количество обрабатываемых элементов n (nÎ[0,N]) запросить у пользователя:

const int N=20;

int a[N],n;

do

{

printf("\nEnter n (1-%d%s",N,"): ");

scanf("%d",&n);

}

while (n<=0 || n>N); // проверка корректности ввода nÎ[0,N]

К элементам массива можно обращаться также с помощью указателей. Указатель – это переменная, значением которой является адрес участка памяти, выделенной для объекта конкретного типа. и являются специальными объектами в языке С. В простейшем случае описание указателя–переменной на некоторый объект имеет вид:

тип *имя_указателя инициализатор;

Например,

int *T; // неинициализированный указатель на объект типа int,

//то есть в переменной T может храниться адрес объекта типа int;

В качестве инициализирующего выражения указателя может использоваться константное выражение, частными случаями которого являются явно заданный адрес участка памяти, указатель, уже имеющий значение, выражение, позволяющее получить адрес объекта с помощью операции ‘&’. Например,

char c1 = ‘a’;

char* p = &c1;// в p хранится адрес переменной c1

Переменная, на которую указывает p, это c1, а значение, которое хранится в c1, это символ ‘a’. Основная операция над указателем – разыменование, унарная операция ‘*’ (получение значения через указатель). Эта операция также называется косвенным обращением или обращением по адресу. Например, при выполнении оператора printf(“%c”, *p); на экран выведется значение того участка памяти, с которым связан указатель p, то есть символ ‘a’.

Имя массива является указателем–константой на его первый элемент. То есть, если описан массив int a [n], то для обращения к его i–ому элементу наряду с записью a[i], можно использовать запись *(a+i).

Например, пусть описан и инициализирован массив a.

const int n=5;

int a[n]={1,2,3,4,5};

Выведем его элементы на экран столбиком тремя способами:

a) int *p=a; for (int i=0;i<n;i++) printf("%d%s",*p++, "\n");

b) for (int i=0;i<n;i++) printf("%d%s",*(a+i), "\n");

c) for (int i=0;i<n;i++) printf("%d%s", a[i], "\n");

Чтобы связать указатель с новым участком памяти, еще не занятым никаким объектом программы, используют функцию malloc (n) из библиотеки stdlib.h, где n – количество выделяемых байт. Например,

char *p= malloc(sizeof(char)); //выделили память для перемененной типа char

//и связали указатель p с эти участком памяти;

Объекты, созданные таким образом (динамические объекты), требуют явного уничтожения с помощью функции free(t) из библиотеки stdlib.h (), где t- имя указателя из-под которого осбождается память, например

free(p);

Функция malloc позволяет определять массив еще одним способом:

int k;

scanf(“%d”,&k);

int *a=malloc(k*sizeof(int));

Этот способ целесообразно использовать, если размерность массива не может быть описана как константа. Однако в данном случае необходимо удалить описанный динамический массив после использования с помощью функции free(а), иначе память, выделенная под него останется занятой до перезагрузки операционной системы. Обращаться к элементам динамического массива можно всеми перечисленными выше способами.

Специальным образом в языке С обрабатываются массивы, называемые С–строками. С–строка – это массив символов, последний из которых символ ‘\0’. Дело в том, что в С нет специального типа данных для описания строк, но есть библиотека функций языка С для работы С–строками (описанная в заголовочном файле string.h, см. задание 15), которые обеспечивают различные операции для манипулирования строками. В этой главе мы рассмотрим работу, используя обычный принцип обработки символов как элементов массива.

При инициализации массив символов может задаваться как последовательность символов, заключенная в двойные кавычки: “...”. Компилятор располагает в конце каждой строки нулевой (пустой) байт, обозначемы esc-последовательностью ‘\0’, с тем, чтобы сканирующая строку программа могла найти ее конец. Например,

char str1[]= “abcd”; // действительная длина этой строки равна 5

char * str2= “abcd”; // действительная длина этой строки равна 5

Кроме того, инициализация строк может быть выполнена следующим образом:

char str3[]={‘a’, ‘b’, ‘c’, ‘d’, ‘\0’};

При такой инициализации списком, а также при посимвольном заполнении неинициализированного массива в цикле в конце символьного массива следует явным образом задать символ конца строки ‘\0’. Только при этом массив получает свойства С–строки, которые можно использовать, например, в библиотечных функциях для работы со строками или при вводе и выводе строки. Например, С–строку str3 в отличии от любого несимвольного массива можно вывести на экран не в цикле, а с помощью с помощью одного оператора printf(“%s”,str2). А неинициализированный массив char str4[20], в отличии от любого несимвольного можно заполнить одним оператором scanf(“%s”, str3); Однако, как отмечено в главе 2, при применении функции scanf клавиатуры считаются в массив str3 все символы только до первого пробела. Чтобы считать строку целиком, можно использовать функцию

gets (указатель_на_строку),

описанную в заголовочном файле stdio.h. Например,

char s1[10]; gets(s1);

char * s2 =malloc(m); scanf(“%s”, str3);

Строка s1 – это массив с константой размерностью 10, элементы которого считываются с клавиатуры с помощью функции gets. Строка s2 – это динамический массив из m элементов, в который считается только m–4 символов (m–3–ий элемент будет равен ‘\0’). Причем второй способ считывания строки предпочтительнее, чем первый, так как функция gets не накладывает ограничения на число считываемых с клавиатуры символов, а это значит, что массив s1 может переполниться.

Пример. Определить длину строки, введенной с клавиатуры, не использую библиотечных функций.

# include <stdio.h>

void main()

{

const int n=255;

char stroka [n];

gets(stroka);//считали строку

int len = 0; // будем использовать переменную len

//как индекс массива

while (stroka[len]) len++; // условие окончание цикла stroka[len]= ‘\0’

printf(“%d”,len);

}

Многомерные массивы описываются как массивы массивов, например,

int a2[3][2]; // массив из 3 массивов, содержащих по 2 целых элемента.

Для обращения к элементу двумерного массива используется два индекса, например, a2[i][j]. Рассмотрим, как используется конструкция вложенных циклов для работы с двумерными массивами и как возможен выход из вложенных циклов.

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

goto идентификатор;

где идентификатор – имя метки оператора, расположенного в той же функции, где используется оператор безусловного перехода. Метка – это обычный идентификатор, после которого ставится двоеточие и следует некоторый оператор. Использование оператора goto принято считать плохим стилем, однако в некоторых случаях его использование может быть действительно обосновано. Например, при решении задачи поиска в матрице размерности (n,m) поиска в матрице хотя бы одного элемента с заданным значением x.

for (int i=0;i<n; i++)

for (int j=0; j<m;j++)

if (a[i][j]==x) goto success;

success:

prinf(“Элемент найден. Строка i= %d”,i);

prinf(“Столбец j= %d”,j);

Также в циклах применяется оператор continue. С его помощь завершается текущая итерация и начинается проверка условий дальнейшего продолжения цикла. Типичный пример использования continue – подсчитать среднее значение только положительных элементов одномерного массива.

for (s=0,k=0,i=0;i<n;i++)

{

if (x[i]<=0) continue;

k++;

s+=x[i];

}

if (k>0) s=s/k;

Каждому студенту рекомендуется выполнить одно из упражнений 1–12 каждого задания 1–6 и все упражнения задания 7. Тестовый пример для данной задачи подготовить в книге MS Excel.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: