Программирование >>  Обобщенные обратные вызовы 

1 ... 59 60 61 [ 62 ] 63 64 65 ... 84


Задача 32. Небольшие очепятки

и прочие курьезы Сложность: 5

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

Попытайтесь ответить на вопросы, не прибегая к помоши компилятора. Вопрос для профессионала

1. Каким будет вывод следующей программы, скомпилированной соответствующим стандарту компилятором?

пример 32-1

#1 nclude <iostream> #include <iomanip>

int mainO { int X = 1;

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

Следующая строка - инкремент???????????/

++x;

std::cout x std::endl;

2. О скольких различных ошибках сообщит соответствующий стандарту компилятор при работе с приведенным исходным текстом?

пример 32-2

struct X {

stati с bool f( int* p ) {

return p && 0[p] and not p[l: p[2];

Решение

1. Каким будет вывод следующей программы, скомпилированной соответствующим стандарту компилятором?

Вывод примера 32-1, если предположить, что в конце строки с комментарием нет невидимых пробельных символов, будет представлять собой 1. Здесь есть две хитрости - одна очевидная и одна более тонкая. Начнем с цикла for.

for С int i =0; i < 100; ++i );

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

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

Давайте внимательно рассмотрим строку с комментарием. Вы заметили, как странно она заканчивается - символом /?



Следующая строка - инкремент???????????/

, Николай Смирнов пишет; Вероятно то, что случилось с программой, очевидно для вас, но я потерял несколько дней на отладку большой программы, в которой допустил подобную ошибку. Я внес строку с комментарием, который заканчивался большим количеством вопросительных знаков, и случайно отпустил клавишу <Shift> раньше времени. В результате случайно получилась последовательность ??/, представляющая собой триграф, который в первой фазе оказался преобразован в символ \, а он в свою очередь во второй фазе обработки исходного текста аннулировал следующий за ним символ \п (Н. Смирнов, частное сообщение).

П ослсдовате л ьн ость ??/ преобразуется в символ \, который, будучи последним символом в строке, представляет собой директиву стыковки строк! В нашем случае такой стыкуемой строкой оказалась строка с инструкцией ++х;, которая теперь оказалась частью комментария, так что инкремент в приведенной программе ни разу не выполняется.

Интересно, что если вы посмотрите документацию по Gnu g++ в части, где говорится об опции командной строки -wtrigraphs, то встретите следующее некорректное обобщение;

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

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

2. О скольких различных ошибках сообщит соответствующий стандарту компилятор при работе с приведенным исходным текстом?

пример 32-2

struct X {

static bool f( int* p ) {

return p && 0[p] and not p[l: p[2];

};

Краткий ответ: ноль. Этот код совершенно корректен и соответствует ставдарту (хочет ли того автор данного исходного текста или нет).

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

Выражение 0[р] совершенно корректно и имеет тот же смысл, что и выражение р[0]. В С (и С-И-) выражение вида х [у], где либо х, либо у имеет тип указателя, а другая величина представляет собой целочисленное значение, всегда означает *(х+у). В нашем случае 0[р] и р[0] имеют один и тот же смысл, поскольку означают *(0+р) и *(р+0) соответственно, что совершенно одинаково. Дополнительную информацию можно найти в [С99] §6.5.2.1.

and и not представляют собой корректные ключевые слова, представляющие собой альтернативную запись операторов && и ! соответственно.

:> представляет собой совершенно корректный дифаф си.мвола ], а не смайлик . Этот диграф превращает последнюю часть выражения в простое неравенство р[1]>р[2].

Поиск в Google по запросу trigraphs within comments дает как этот, так и несколько других интересных и/или забавных трюков.



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

class X { ;;;;;;;;;; };

Для большинства программистов наибольший сюрприз из представленных здесь преподносит диграф :>. Конечно, возможно, здесь вкралась опечатка и автор на самом деле имел в виду нечто иное, например, р[1] р[2], но даже если это и опечатка, то исходный текст и с ней (в таком случае - к сожалению) остается совершенно корректным.



1 ... 59 60 61 [ 62 ] 63 64 65 ... 84

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика