Оператор goto и метки в Pascal

Создано: 05.04.2026

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

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

var
    f: real;
    error: integer;
    s: string;

begin
    readln(s);
    val(s, f, error);

    if error <> 0 then
        writeln('Введено не число!')
    else
        writeln(sqrt(f):10:3);
end.
45.24
     6.726
4a
Введено не число!

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

var
    f: real;
    error: integer;
    s: string;

begin
    readln(s);
    val(s, f, error);

    if error <> 0 then begin
        writeln('Введено не число!');
        readln(s);
        val(s, f, error);
        if error <> 0 then 
            writeln('Введено не число!')
        else
            writeln(sqrt(f):10:3);
    end
    else
        writeln(sqrt(f):10:3);
end.

Программа станет нелепой, если попыток будет не две, а больше. Вообще, может мы хотим, чтобы пользователь вводил данные до тех пор, пока они не окажутся корректными. Как быть в этом случае?

В современном программировании задача повторения действий решается с помощью циклов. Мы их будем подробно изучать в следующих уроках. Однако на заре становления программирования, когда до циклов еще не додумались, вместо них использовался оператор безусловного перехода goto. На сегодняшний день он остался в некоторых языках программирования, в том числе Паскале, и в редких случаях бывает полезен. Следует отметить, что if и циклы — это операторы условного перехода, так как они обеспечивают "прыжки" по коду в зависимости от условия. В то время как при самом goto никаких логических выражений нет.

Вместо условия после goto пишется имя метки, куда компьютер–исполнитель должен перейти. Эта метка должна где-то находится в разделе действий программы. Кроме этого, чтобы можно было использовать метку, она должна быть в обязательном порядке объявлена в разделе меток в описательной части программы. Этот раздел начинается ключевым словом label, после которого через запятую перечисляются метки. В качестве имени метки может быть использовано любое целое число без знака или любое имя.

Посмотрим, как это выглядит в коде:

label
    input;

var
    f: real;
    error: integer;
    s: string;

begin
    input:
    readln(s);
    val(s, f, error);

    if error <> 0 then begin
        writeln('Введите число!');
        goto input
    end        
    else
        writeln(sqrt(f):10:3);
end.

Оператор goto осуществляет переход к оператору, помеченному указанной меткой, которая отделяется от самого оператора двоеточием. Так в примере, когда выполняется команда goto input, происходит переход к процедуре readln(s), потому что именно перед ней стоит такая метка с двоеточием.

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

if error <> 0 then begin
    writeln('Введите число!');
    goto input
end;    

writeln(sqrt(f):10:3);

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

label
    input;

var
    f: real;
    error: integer;
    s: string;

begin
    input:
    readln(s);
    val(s, f, error);

    if error <> 0 then begin 
        writeln('Введите число!');
        goto input
    end;  

    if f < 0 then begin
        writeln('Нельзя извлечь корень из отрицательного!');
        goto input
    end;   

    writeln(sqrt(f):10:3);
end.

Перепишите программу так, чтобы вместо двух условных операторов был один со сложным логическим выражением. Таким образом, если вводится не число или отрицательное значение, программа на оба случая должна выводить фразу "Введите неотрицательное число!".

Рассмотрим другой пример. Здесь не станем проверять корректность ввода, а примем за факт, что всегда вводятся целые числа. Вместо этого сложность программы будет заключаться в необходимости сложения пяти чисел, введенных с клавиатуры. Хотя, кажется, что может быть проще:

var
    a, b, c, d, e: integer;

begin
    readln(a, b, c, d, e);   

    writeln('Сумма: ', a+b+c+d+e);
end.

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

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

var
    a, sum: integer;

begin
    sum := 0;

    read(a); 
    sum := sum + a;
    read(a); 
    sum := sum + a;
    read(a); 
    sum := sum + a;
    read(a); 
    sum := sum + a;
    read(a); 
    sum := sum + a;

    writeln('Сумма: ', sum);
end.

Но это тоже плохое решение, так как приводит к многократному дублированию исходного кода.

Теперь посмотрим, как эта программа будет выглядеть с меткой:

label
    number;

var
    i, n, sum: integer;

begin
    i := 0;
    sum := 0;

    number:
    if i < 5 then begin
        read(n);
        sum := sum + n;
        i := i + 1;
        goto number;
    end;    

    writeln('Сумма: ', sum);
end.
4 5 8 12 5
Сумма: 34

Здесь пришлось ввести переменную i, играющую роль счетчика запросов числа с ввода. Пока значение счетчика будет оставаться меньше пяти, из тела условного оператора мы будем переходить к его началу. По-сути у нас уже получился цикл. Если захочется сложить не пять чисел, а десять, достаточно поменять число в логическом выражении при if.

Когда счетчик достигает установленного предела, условие становится ложным, поток выполнения не заходит в тело if, и команда goto number больше не исполняется.

В коде может быть не одна метка, поэтому программу можно переписать таким образом:

label
    startLoop, endLoop;

var
    i, n, sum: integer;

begin
    i := 0;
    sum := 0;

    startLoop:
    if i >= 5 then goto endLoop;
    readln(n);
    sum := sum + n;
    i := i + 1;
    goto startLoop;

    endLoop:
    writeln('Сумма: ', sum);
end.

В таком варианте, можно сказать, что по условию происходит выход из повторяемого блока кода, а не вход в него. Если убрать строчку с условным оператором, "цикл" станет бесконечным.

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

Задания для дополнительной или самостоятельной работы:

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