Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебник по С++.docx
Скачиваний:
1
Добавлен:
01.04.2025
Размер:
841.08 Кб
Скачать

3.3.2 Оператор goto

Презираемый оператор goto все-таки есть в С++:

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

идентификатор: оператор

Вообще говоря, он мало используется в языках высокого уровня, но

может быть очень полезен, если текст на С++ создается не человеком,

а автоматически, т.е. с помощью программы. Например,

операторы goto используются при создании анализатора по заданной

грамматике языка с помощью программных средств.

Кроме того, операторы goto могут пригодиться в тех случаях,

когда на первый план выходит скорость работы программы. Один из

них - когда в реальном времени происходят какие-то вычисления во

внутреннем цикле программы.

Есть немногие ситуации и в обычных программах, когда применение

goto оправдано. Одна из них - выход из вложенного цикла или

переключателя. Дело в том, что оператор break во вложенных циклах

или переключателях позволяет перейти только на один уровень выше.

Приведем пример:

void f()

{

int i;

int j;

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

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

if (nm[i][j] == a) goto found;

// здесь a не найдено

// ...

found:

// nm[i][j] == a

}

Есть еще оператор continue, который позволяет перейти на конец

цикла. Что это значит, объяснено в $$3.1.5.

3.4 Комментарии и расположение текста

Программу гораздо легче читать, и она становится намного понятнее, если

разумно использовать комментарии и систематически выделять текст

программы пробелами. Есть несколько способов расположения текста

программы, но нет причин считать, что один из них - наилучший. Хотя

у каждого свой вкус. То же можно сказать и о комментариях.

Однако можно заполнить программу такими комментариями, что читать

и понимать ее будет только труднее. Транслятор не в силах понять

комментарий, поэтому он не может убедиться в том, что комментарий:

[1] осмысленный,

[2] действительно описывает программу,

[3] не устарел.

Во многих программах попадаются непостижимые, двусмысленные и просто

неверные комментарии. Лучше вообще обходиться без них, чем давать

такие комментарии.

Если некий факт можно прямо выразить в языке, то так и следует

делать, и не надо считать, что достаточно упомянуть его в комментарии.

Последнее замечание относится к комментариям, подобным приведенным

ниже:

// переменную "v" необходимо инициализировать.

// переменная "v" может использоваться только в функции "f()".

// до вызова любой функции из этого файла

// необходимо вызвать функцию "init()".

// в конце своей программы вызовите функцию "cleanup()".

// не используйте функцию "weird()".

// функция "f()" имеет два параметра.

При правильном программировании на С++ такие комментарии обычно

оказываются излишними. Чтобы именно эти комментарии стали ненужными,

можно воспользоваться правилами связывания ($$4.2) и областей

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

класса ($$5.5).

Если некоторое утверждение выражается самой программой, не нужно

повторять его в комментарии. Например:

a = b + c; // a принимает значение b+c

count++; // увеличим счетчик count

Такие комментарии хуже, чем избыточные. Они раздувают объем текста,

затуманивают программу и могут быть даже ложными. В то же время

комментарии именно такого рода используют для примеров в учебниках

по языкам программирования, подобных этой книге. Это одна из

многих причин, по которой учебная программа отличается от настоящей.

Можно рекомендовать такой стиль введения комментариев в

программу:

[1] начинать с комментария каждый файл программы: указать в

общих чертах, что в ней определяется, дать ссылки на

справочные руководства, общие идеи по сопровождению

программы и т.д.;

[2] снабжать комментарием каждое определение класса или шаблона

типа;

[3] комментировать каждую нетривиальную функцию, указав: ее

назначение, используемый алгоритм (если только он неочевиден)

и, возможно, предположения об окружении, в котором работает

функция;

[4] комментировать определение каждой глобальной переменной;

[5] давать некоторое число комментариев в тех местах, где

алгоритм неочевиден или непереносим;

[6] больше практически ничего.

Приведем пример:

// tbl.c: Реализация таблицы имен.

/*

Использован метод Гаусса

см. Ральстон "Начальный курс по ..." стр. 411.

*/

// в swap() предполагается, что стек AT&T начинается с 3B20.

/************************************

Авторские права (c) 1991 AT&T, Inc

Все права сохранены

**************************************/

Правильно подобранные и хорошо составленные комментарии играют в

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

трудно, чем саму программу, и это - искусство, в котором стоит

совершенствоваться.

Заметим, что если в функции используются только комментарии

вида //, то любую ее часть можно сделать комментарием с помощью

/* */, и наоборот.

3.5 Упражнения

1. (*1) Следующий цикл for перепишите с помощью оператора while:

for (i=0; i<max_length; i++)

if (input_line[i] == '?') quest_count++;

Запишите цикл, используя в качестве его управляющей переменной

указатель так, чтобы условие имело вид *p=='?'.

2. (*1) Укажите порядок вычисления следующих выражений, задав полную

скобочную структуру:

a = b + c * d << 2 & 8

a & 077 != 3

a == b || a == c && c < 5

c = x != 0

0 <= i < 7

f(1,2) + 3

a = - 1 + + b -- - 5

a = b == c ++

a = b = c = 0

a[4][2] *= * b ? c : * d * 2

a-b, c=d

3. (*2) Укажите 5 различных конструкций на С++, значение которых

неопределено.

4. (*2) Приведите 10 разных примеров непереносимых конструкций

на С++.

5. (*1) Что произойдет при делении на нуль в вашей программе на С++?

Что будет в случае переполнения или потери значимости?

6. (*1) Укажите порядок вычисления следующих выражений, задав их

полную скобочную структуру:

*p++

*--p

++a--

(int*)p->m

*p.m

*a[i]

7. (*2) Напишите такие функции: strlen() - подсчет длины строки,

strcpy() - копирование строк и strcmp() - сравнение строк. Какими

должны быть типы параметров и результатов функций? Сравните их

со стандартными версиями, имеющимися в <string.h> и в вашем

руководстве.

8. (*1) Выясните, как ваш транслятор отреагирует на такие ошибки:

void f(int a, int b)

{

if (a = 3) // ...

if (a&077 == 0) // ...

a := b+1;

}

Посмотрите, какова будет реакция на более простые ошибки.

9. (*2) Напишите функцию cat(), которая получает два параметра-строки

и возвращает строку, являющуюся их конкатенацией. Для

результирующей строки используйте память, отведенную с помощью

new. Напишите функцию rev() для перевертывания строки, переданной

ей в качестве параметра. Это означает, что после вызова rev(p)

последний символ p станет первым и т.д. 10. (*2) Что делает следующая функция?

void send(register* to, register* from, register count)

// Псевдоустройство. Все комментарии сознательно удалены

{

register n=(count+7)/8;

switch (count%8) {

case 0: do { *to++ = *from++;

case 7: *to++ = *from++;

case 6: *to++ = *from++;

case 5: *to++ = *from++;

case 4: *to++ = *from++;

case 3: *to++ = *from++;

case 2: *to++ = *from++;

case 1: *to++ = *from++;

} while (--n>0);

}

}

Каков может быть смысл этой функции?

11. (*2) Напишите функцию atoi(), которая имеет параметр - строку цифр

и возвращает соответствующее ей целое. Например, atoi("123")

равно 123. Измените функцию atoi() так, чтобы она могла

переводить в число последовательность цифр не только в десятичной,

но и в восьмеричной и шестнадцатеричной записи, принятой в С++.

Добавьте возможность перевода символьных констант С++. Напишите

функцию itoa() для перевода целого значения в строковое

представление.

12. (*2) Перепишите функцию get_token() ($$3.12) так, чтобы она читала

целую строку в буфер, а затем выдавала лексемы, читая по

символу из буфера.

13. (*2) Введите в программу калькулятора из $$3.1 такие функции, как

sqrt(), log() и sin(). Подсказка: задайте предопределенные

имена и вызывайте функции с помощью массива указателей на них.

Не забывайте проверять параметры, передаваемые этим

функциям.

14. (*3) Введите в калькулятор возможность определять пользовательские

функции. Подсказка: определите функцию как последовательность

операторов, будто бы заданную самим пользователем. Эту

последовательность можно хранить или как строку символов, или

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

выполнять операции. Если пользовательские функции могут

иметь параметры, то придется придумать форму записи и для них.

15. (*1.5) Переделайте программу калькулятора, используя структуру

symbol вместо статических переменных name_string и number_value:

struct symbol {

token_value tok;

union {

double number_value;

char* name_string;

};

};

16.(*2.5) Напишите программу, которая удаляет все комментарии из

программы на С++. Это значит, надо читать символы из cin и

удалять комментарии двух видов: // и /* */. Получившийся текст

запишите в cout. Не заботьтесь о красивом виде получившегося

текста (это уже другая, более сложная задача). Корректность

программ неважна. Нужно учитывать возможность появления символов

//, /* и */ в комментариях, строках и символьных константах.

17. (*2) Исследуйте различные программы и выясните, какие способы

выделения текста пробелами и какие комментарии используются.

* ГЛАВА 4

Итерация присуща человеку,

а рекурсия - богу.

- Л. Дойч

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