Нижегородский государственный университет им. Н.И.Лобачевского
Опубликован: 25.11.2008 | Доступ: свободный | Студентов: 9593 / 1296 | Оценка: 4.06 / 3.66 | Длительность: 21:16:00
Лекция 6:

Основные синтаксические конструкции языка C

< Лекция 5 || Лекция 6: 1234 || Лекция 7 >

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

Перед любым исполняемым оператором программы может находиться символьная метка, отделяемая от оператора двоеточием:

m5: printf("\nx=%f",x);

На помеченный таким образом оператор может быть передано управление с помощью оператора goto m5. Но такие переходы допустимы только внутри функции. Переход из одной функции в другую по оператору goto недопустим.

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

Рассмотрим две реализации программы, которая вводит три целочисленных значения и выводит максимальное из них. В первой из них (программа-спагетти) на 10 исполняемых строк приходится 5 операторов goto. Во второй реализации потребовалось всего 5 исполняемых строк и ни одного оператора goto.

Пример 5.1. Программа-спагетти.

#include <stdio.h>
#include <conio.h>
void main()
{ int a,b,c;
  scanf("%d %d %d",&a,&b,&c);
  if(a>b) goto m1;
  if(b>c) goto m2;
m3: printf("max=%d",c);
  goto m5;
m1: if(c>a) goto m3;
m4: printf("max=%d",a); 
  goto m5;
m2: printf("max=%d",b);
m5: getch();
}

Пример 5.2. Пример прозрачной логики

#include <stdio.h>
#include <conio.h>
void main()
{ int a,b,c;
  scanf("%d %d %d",&a,&b,&c);
  if(b>a) a=b;
  if(c>a) a=c;
  printf("max=%d",a);
  getch();
}

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

int i,j;
for(i=0; i<n; i++) {...
  for(j=0; j<20; j++) {...
    if(условие_выхода) goto mm;
  }
}
mm:

Конечно, можно придумать более запутанную схему, не используя goto:

int i,j,k=1;
for(i=0; i<n && k; i++) {...
  for(j=0; j<20 && k; j++) {...
    if(условие_выхода) {k=0; break;}
  }
}

Применяя оператор goto, стоит следовать следующим рекомендациям:

  • не входить внутрь блока извне, особенно если в начале этого блока присутствуют объявления локальных переменных с инициализацией;
  • не входить в цикл извне, ибо это приведет к неправильной работе цикла;
  • не передавать управление внутрь условного оператора;
  • не передавать управление внутрь переключателя.

5.7. Операторы цикла

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

Наиболее часто применяемая конструкция цикла использует оператор for:

for(S1; C; S2) { Q1; Q2;...}

Здесь

  • S1 – действие, которое выполняется перед повторением группы операторов Q1, Q2, ..., образующих тело цикла;
  • C – условие, при выполнении которого тело цикла повторяется. Если условие C не выполнено, то тело цикла обходится и цикл завершается;
  • S2 – действие, которое выполняется вслед за последним оператором тела цикла. После этого управление передается на проверку условия завершения цикла.

Одна из форм оператора for позволяет организовать повторение тела цикла заданное число раз, изменяя при каждом повторении значение некоторой управляющей переменной (иногда такую переменную называют счетчиком цикла или индексом цикла):

s=0;
  for(j=0; j<10; j++)
    s=s+a[j];

В приведенном примере тело цикла состоит из единственного оператора, который можно не заключать в фигурные скобки. Перед началом цикла в переменную j заносится 0 и поскольку это значение меньше 10, то к содержимому переменной s прибавляется значение первого элемента массива a[0]. После первого завершения тела цикла к счетчику j добавляется 1 и тело цикла повторяется. Так продолжается до тех пор, пока значение счетчика не увеличится до 10, при котором условие повторения цикла уже не выполняется.

Если счетчик цикла используется только в теле цикла и значение этой переменной за пределами цикла сохранять не нужно, то стандарт C++ позволяет определить такую суперлокальную переменную непосредственно в операторе for:

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

Значение счетчика (индекса) может не только увеличиваться при каждом повторении цикла, но и уменьшаться:

s=0;
  for(j=9; j>=0; j--)
    s=s+a[j];

В этом примере массив a суммируется, начиная с последнего элемента.

Иногда цикл по счетчику в обратном порядке организуют следующим образом:

count=20;
  for(s=0; count; count--)
    s += a[count];

В этом примере условием повторения цикла является положительное значение счетчика count, которое воспринимается как "истина". Как только содержимое счетчика станет равным нулю (т.е. "лжи"), цикл завершит свою работу.

В отличие от языка Паскаль, где оператор for допускает только увеличение или уменьшение счетчика цикла только на 1, оператор for в языках C, C++ позволяет изменять значение управляющей переменной произвольным образом. Эта переменная может быть вещественного типа, и ее значение может меняться не только по закону арифметической прогрессии, но и любым сколь угодно сложным способом. Например, можно просуммировать только элементы массива с четными индексами:

s=0;
  for(j=0; j<10; j+=2)
    s=s+a[j];

Действие S1, выполняемое при входе в цикл, может состоят не только из единственного оператора, засылающего в счетчик цикла начальное значение. Если нужно выполнить несколько операторов, то их разделяют запятыми (список действий, аналогичный экзотической форме оператора присваивания). В приведенных выше примерах очистку переменной s тоже можно было внести в оператор заголовка цикла:

for(s=0,j=0; j<10; ++j)
    s=s+a[j];

Аналогичный список из нескольких операторов можно использовать и в качестве действия S2, выполняемого вслед за последним оператором тела цикла. В приведенном ниже примере оператор цикла вычисляет n-й член последовательности чисел Фибоначчи:

for(x=1,y=1,i=2; i<n; z=x+y, x=y, y=z, i++);

Иногда в программах можно встретить на первый взгляд бессмысленный оператор for:

for(;;){Q1;Q2;...;}

Судя по описанию оператора for, тело такого цикла должно выполняться бесконечное число раз. Однако такие циклы могут оказаться полезными в DOS-приложениях для организации ожидания какого-либо события, появление которого проверяется в теле цикла. Если событие произошло (например, ввод информации со стороны пользователя состоялся, или по каналу связи поступило долгожданное сообщение), то из тела цикла можно выйти с помощью оператора break или goto. В среде Windows существуют и более эффективные (с точки зрения загрузки процессора) средства, контролирующие появление того или иного события.

Вторая конструкция оператора цикла начинается со служебного слова while (от англ. – пока):

while(условие) {Q1; Q2;...}

Вход в такой цикл начинается с проверки условия, заданного в круглых скобках. Если это условие истинно, то тело цикла (операторы Q1, Q2,...) выполняется. В случае невыполнения условия тело цикла обходится. С помощью оператора while тоже можно организовать суммирование элементов массива:

s=0;  i=0;
  while(i<10)
    { s=s+a[i]; i++; }

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

yn+1=0.5*(yn +x/yn)

Для ее реализации можно воспользоваться следующим циклом:

y=x; //начальное приближение
  while(fabs(x-y*y)>1e-6)
    y=0.5*(y+x/y);

Третья конструкция цикла начинается с оператора do (от англ. – выполнить) и завершается проверкой условия продолжения цикла while:

y=x; //начальное приближение
  do
    y=0.5*(y+x/y);
  while(fabs(x-y*y)>1e-6)

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

Все конструкции операторов цикла допускают вложение других циклов в свои тела. Например, для умножения квадратных матриц можно воспользоваться следующим фрагментом программы:

for(i=0; i<n; i++)
  for(j=0; j<n; j++)
    {for(d=0,k=0; k<n; k++)
       d += a[i][k]*b[k][j];
     c[i][j]=d;
    }
< Лекция 5 || Лекция 6: 1234 || Лекция 7 >
Alexey Ku
Alexey Ku

Попробуйте часть кода до слова main заменить на 

#include "stdafx.h" //1

#include <iostream> //2
#include <conio.h>

using namespace std; //3

Александр Талеев
Александр Талеев

#include <iostream.h>
#include <conio.h>
int main(void)
{
int a,b,max;
cout << "a=5";
cin >> a;
cout <<"b=3";
cin >> b;
if(a>b) max=a;
else max=b;
cout <<" max="<<max;
getch();
return 0;
}

при запуске в visual express выдает ошибки 

Ошибка    1    error C1083: Не удается открыть файл включение: iostream.h: No such file or directory    c:\users\саня\documents\visual studio 2012\projects\проект3\проект3\исходный код.cpp    1    1    Проект3

    2    IntelliSense: не удается открыть источник файл "iostream.h"    c:\Users\Саня\Documents\Visual Studio 2012\Projects\Проект3\Проект3\Исходный код.cpp    1    1    Проект3

    3    IntelliSense: идентификатор "cout" не определен    c:\Users\Саня\Documents\Visual Studio 2012\Projects\Проект3\Проект3\Исходный код.cpp    6    1    Проект3

    4    IntelliSense: идентификатор "cin" не определен    c:\Users\Саня\Documents\Visual Studio 2012\Projects\Проект3\Проект3\Исходный код.cpp    7    1    Проект3

при создании файла я выбрал пустой проект. Может нужно было выбрать консольное приложение?