Операторы цикла. Оператор безусловного перехода, break, continue. 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Операторы цикла. Оператор безусловного перехода, break, continue.



Оператор цикла while

Оператор цикла с предусловием имеет вид while (выражение) оператор

Оператор называют телом цикла.

При выполнении такого оператора сначала вычисляется значение выражения. Если оно равно 0, то оператор не выполняется и управление передается оператору, следующему за ним. Если значение выражения отлично от 0, то выполняется оператор, затем снова вычисляется выражение и т.д.

Возможно, что тело цикла не выполнится ни разу, если выражение сразу будет равно 0.

Пример 1: char с;
while (cin.get(c)) cout<< c;

Здесь происходит копирование символов, включая пробельные, из потока cin (в данном случае из буфера клавиатуры) в поток cout (в данном случае на экран дисплея). Здесь функция get(c) (член класса) извлекает один символ из входного потока, включая пробельные символы. Возвращает ненулевое значение до тех пор, пока не достигнет конца файла (признак конца файла - ctrl-z).

Пример 2:

while (1) { операторы... } Это - бесконечный цикл.

Пример 3:

char c;
while ((c = cin.get (c)) = = ' ' || c = = '\n' || c = = '\t');

Этот оператор цикла пропускает при считывании из потока cin так называемые пробельные символы. Здесь get() - другая форма функции, считывающей из потока один символ. Она возвращает целое число - код символа, или число -1, если встретился признак конца файла.

Цикл с постусловием do-while

Этот оператор цикла проверяет условие окончания в конце, после каждого прохода через тело цикла; поэтому тело цикла всегда выполняется по крайней мере один раз.

Вид оператора: do оператор while (выражение)

Сначала выполняется оператор, затем вычисляется выражение и, если оно отлично от нуля, то оператор выполняется снова и т.д.

Если выражение становится равно нулю, цикл завершается.

Такой цикл удобно использовать при проверке вводимых данных:

int input=0;
int minvalue=10, maxvalue=150;
do { cout <<"Введите значение input \n"; cin >>input;
cout <<" input=" << input << "\n"; }
while (input < minvalue || input > maxvalue);

Оператор безусловного перехода

Оператор безусловного перехода имеет вид goto метка;

Метка - это имя, за которым следует ':'. Этот оператор передает управление оператору, помеченному указанной меткой. С его помощью удобно выходить сразу из нескольких вложенных циклов:

for (i = 0; i < n; i++)
for (j = 0; j < m; j++)
for (k = 0;k < l; k++) {
...
операторы
...
if (условие) goto lab;
операторы;}
lab:;...

С помощью оператора goto можно переходить извне в тело блока, если при этом управление не передается через объявления имен, которые присутствуют в этом блоке.

Оператор break

Этот оператор осуществляет выход из тела цикла for, while, do-while или оператора switch, в котором он появился. При этом управление передается на первый оператор после цикла.

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

Оператор continue

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

Пример вывода четных чисел:

for (int num = 0; num < 100; num++){
if (num % 2) continue;
cout << num << "\n";
}

Когда num становится нечетным, выражение num % 2 получает значение 1 и выполняется оператор, который передает управление на следующую итерацию цикла for, не выполняя вывода.


Указатели. Указатели и массивы. Адресная арифметика.

Определение указателей

Указатель - это переменная, содержащая адрес некоторого объекта, например, другой переменной. Точнее - адрес первого байта этого объекта. Это дает возможность косвенного доступа к этому объекту через указатель. Пусть x - переменная типа int. Обозначим через px указатель. Унарная операция & выдает адрес объекта, так что оператор

px = &x;

присваивает переменной px адрес переменной x. Говорят, что px "указывает" на x. Операция & применима только к адресным выражениям, так что конструкции вида &(x -1) и &3 незаконны.

Унарная операция * называется операцией разадресации или операцией разрешения адреса. Эта операция рассматривает свой операнд как адрес и обращается по этому адресу, чтобы извлечь объект, содержащийся по этому адресу.

Следовательно, если y тоже имеет тип int, то

y = *px;

присваивает y содержимое того, на что указывает px. Так, последовательность

px = &x;
y = *px;

присваивает y то же самое значение, что и оператор

y = x;

Все эти переменные должны быть описаны:

int x, y;
int *px;

Последнее - описание указателя. Его можно рассматривать как мнемоническое. Оно говорит, что комбинация *px имеет тип int или, иначе, px есть указатель на int. Это означает, что если px появляется в виде *px, то это эквивалентно переменной типа int.

Из описания указателя следует, что он может указывать только на определенный вид объекта (в данном случае int). Разадресованный указатель может входить в любые выражения там, где может появиться объект того типа, на который этот указатель указывает. Так, оператор

y = *px + 2;

присваивает y значение, на 2 больше, чем х.

Заметим, что приоритет унарных операций * и & таков, что эти операции связаны со своими операндами более крепко, чем арифметические операции, так что выражение

y = *px + 2

берет то значение, на которое указывает px, прибавляет 2 и присваивает результат переменной y.

Если px указывает на х, то

*px = 3;

полагает х равным 3, а

*px + = 1;

увеличивает х на 1, также как и выражение

(*px) ++

Круглые скобки здесь необходимы. Если их опустить, то есть написать *px ++, то, поскольку унарные операции, подобные * и ++, выполняются справа - налево, это выражение увеличит px, а не ту переменную, на которую он указывает.

Если py - другой указатель на int, то можно выполнить присвоение py = px;

Здесь адрес из px копируется в py. В результате py указывает на то же, что и px.

Указатели и массивы

Массив - это совокупность элементов одного типа, которые расположены в памяти ЭВМ подряд, один за другим.

Признаком объявления массива являются квадратные скобки. Объявить массив из 10 элементов типа float можно так:

float a[10];

Чтобы обратиться к элементу этого массива, нужно применить операцию индексирования a[ind]. Внутри квадратных скобок помещается целое выражение, которое называется индексом. Нумерация элементов массива начинается с 0 и поэтому вышеприведенное описание говорит о том, что в памяти ЭВМ зарезервировано место под 10 переменных типа float и эти переменные есть a [0], a [1],..., a [9].

Приведем пример с использованием массива.

Напишем программу подсчета числа появлений каждой цифры, пробельных символов и всех остальных символов.

Число пробельных символов будем хранить в nwhite, прочих символов - в nother, а число появлений каждой из цифр - в массиве ndigit:

# include < iostream.h>
void main() {
int c, i, nwhite = 0, nother = 0;
int ndigit [10];
for (i=0; i<10; i++) ndigit[i]=0;
while ((c=cin.get())!=EOF)
if(c>='0' && c<='9') ++ ndigit[c - '0'];
else if (c = = ' '|| c = = '\n' || c = = '\t') ++ nwhite;
else ++ nother;
cout<<" цифра \n";
for(i=0; i<10; i ++)
cout<< i<<" вошла"<< ndigit[i]<< " раз \n";
cout<< " пробельных символов - "<< nwhite << " прочих символов - "
<< nother << "\n"; }

При объявлении массива его можно инициализировать:

int c[ ] = { 1, 2, 7, 0, 3, 5, 5 };
char array[ ] = { 'h', 'e', 'l', 'l', 'o', '\n', '\0'};

Последнюю инициализацию разрешается выполнять проще:

char array[ ] = "hello\n";

Такой синтаксис инициализации разрешен только для строк. Компилятор сам вычислит необходимый размер памяти с учетом автоматически добавляемого в конец строки символа '\0' с кодом 0, который является признаком завершения строки.

В языке С++ имя массива является константным указателем на первый элемент этого массива:

int mas[20];
int *pmas;
pmas = &mas[0];

Последний оператор можно записать и так: pmas = mas;

Операция индексирования массива [ ] имеет 2 операнда - имя массива, т.е. указатель, и индекс, т.е. целое: a[i]. В языке С++ любое выражение указатель[индекс] по определению трактуется как

*(указатель + индекс)

и автоматически преобразуется к такому виду компилятором.

Таким образом, a[3] эквивалентно *(a + 3). Более того, это можно записать даже так 3[a], т.к. это все равно будет проинтерпретировано как *(3+a). Здесь складываются указатель a и целое 3. В связи с этим рассмотрим так называемую адресную арифметику.

Адресная арифметика

Указатель можно складывать с целым.

Если к указателю pa прибавляется целое приращение i, то приращение масштабируется размером памяти, занимаемой объектом, на который указывает указатель pa.

Таким образом, pa+i - это адрес i -го элемента после pa, причем считается, что размер всех этих i элементов равен размеру объекта, на который указывает pa.

Итак, если a - массив, то
a+i - адрес i-го элемента этого массива, т.е.
&a[i] равен a+i и a[i] равняется *(a+i).

float b[10];
float *pb=b;
pb++; // Это эквивалентно pb=pb_1.
// Здесь указатель pb будет указывать
// на элемент массива b[1].
pb+=3; // Здесь pb указывает на элемент массива b[4].

Отметим, что нельзя написать b++ или b = b+i, т.к. имя массива b - это константный указатель и его изменять нельзя.

Указатели можно сравнивать.

Если p и q указывают на элементы одного и того же массива, то такие отношения, как < >= и т.д. работают надлежащим образом. Например,

p < q истинно, т.е. = = 1, если p указывает на более ранний элемент массива, чем q. Любой указатель можно сравнить на равенство или неравенство с так называемым нулевым указателем NULL, который ни на что не указывает. Однако не рекомендуется сравнивать указатели, указывающие на различные массивы.

Указатели можно вычитать.

Если p и q указывают на элементы одного и того же массива, то p - q дает количество элементов массива между p и q.


8. Символьные массивы и строки. Указатели и многомерные массивы.

Символьные массивы и строки

Строка являются массивом символов. Значением строки является указатель на первый ее символ:

char *string = "строка\n";

Здесь указатель на символы string будет содержать адрес первого символа 'c' строки "строка\n", которая размещается в некоторой области памяти, начиная с этого адреса:

Здесь string [3] = = 'о'.

Рассмотрим фрагмент программы:

char buffer[ ] =" "; // Инициализация
// строки из 10 пробелов.
char *string = buffer; // string указывает на начало буфера.
string="проба\n"; // Присваивание!

При инициализации создается строка buffer и в нее помещаются символы (здесь 10 пробелов). Инициализация char *string=buffer устанавливает указатель string на начало этой строки.

Операция же присваивания в последней строке не копирует приведенную строку "проба\n" в массив buffer, а изменяет значение указателя string так, что он начинает указывать на строку "проба\n":

Чтобы скопировать строку "проба\n" в buffer, можно поступить так:

char buffer[ ] = " ";
char *p ="проба\n";
int i =0;
while ((buffer[i] = p[i])!= '\0') i++;

Или так:
char buffer[ ] = " "
char * p = "проба\n";
char * buf = buffer;
while (*buf ++ = *p ++);

Здесь сначала *p копируется в *buf, т.е. символ 'п' копируется по адресу buf, который совпадает с адресом buffer, т.е. buffer [0] становится равен 'п'. Затем происходит увеличение указателей p и buf, что приводит к продвижению по строкам "проба\n" и buffer соответственно. Последний скопированный символ будет '\0', его значение - 0 и оператор while прекратит цикл.

Еще проще воспользоваться библиотечной функцией, прототип которой находится в файле string.h:

strcpy(buffer, "проба\n");

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

Указатели и многомерные массивы

Рассмотрим разницу между объектами a и b, описанными следующим образом:

int a[10][10];
int * b[10];

И a и b можно использовать сходным образом в том смысле, что как a [5][5], так и b [5][5] являются обращениями к отдельному значению типа int. Но a - настоящий массив: под него отводится 100 ячеек памяти и для нахождения любого указанного элемента проводятся обычные вычисления с индексами, которые требуют умножения. Для b описание выделяет только 10 указателей. Каждый из них должен быть установлен так, чтобы он указывал на массив целых.

Если предположить, что каждый из них указывает на массив из 10 элементов, то тогда где-то будет отведено 100 ячеек памяти плюс еще 10 ячеек для указателей. Таким образом, массив указателей использует несколько больший объем памяти и может требовать наличие явного шага инициализации. Но при этом возникают 2 преимущества: доступ к элементу осуществляется косвенно через указатель, а не посредством умножения и сложения, и строки массива могут иметь различные длины. Это означает, что каждый элемент b не должен обязательно указывать на вектор из 10 элементов. Эту разницу можно увидеть в следующем примере.

char day [5][12] = {
"понедельник", // В каждой строке 12 символов.
"вторник",
"среда",
"четверг",
"пятница"
};

Здесь константные указатели day[0], day[1], …, day[4] адресуют участки памяти одинаковой длины 12 байт каждый:

char * day1[2] = { “суббота”, // 7 символов + ‘/0’
“воскресенье”}; // 11 символов + ‘/0’

Здесь переменные-указатели day1[0] и day1[1] адресуют участки памяти соответственно в 8 и 12 байт.

 

 



Поделиться:


Последнее изменение этой страницы: 2016-08-15; просмотров: 480; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 13.59.236.219 (0.04 с.)