Форум » C/C++ » Код, который не следует писать, но нужно знать! » Ответить

Код, который не следует писать, но нужно знать!

Сыроежка: На создание этой темы меня подвигли достаточно курьезный и забавный код и соответсвующий баг компилятора Intel. Исходное описание проблемы, приведенное Alexey Sudachen, можно прочитать по ссылке http://www.rsdn.ru/?/forum/cpp.applied/ Чем так забавен и курьезен этот случай? Забавен он тем, что код из примера, во-первых, вряд ли когда-нибудь встретишь в реальном проекте (хотя автор исходной темы утверждает обратное), и, во-вторых, с моей точки зрения такой код не следует вообще никогда писать. Почему не следует никогда писать такой код? Потому что он нарушает принципы структурного программирования, и конструкция структурного программирования switch превращается совершенно в неструктурную конструецию, имитирующую оператор goto. То есть в примере, который будет приведен ниже, фактически, с помощью завуалированного оператора goto управление передается внутрь структурных конструкций, как, например, управляющего предложения while. Но хуже того такой код очень тяжело воспринимать, и, чтобы понять, что этот код делает, а также чтобы правильно его трактовать, потребуется немало умственных усилий и знание стандарта языка С. Так как обычно такой код редко, когда встретишь, то есть когда управление с помощью завуалированного оператора goto передается внутрь управляющей конструкции, то многие программисты сходу не в состоянии оценить все семантические тонкости таких конструкций и более того даже не могут сказать, является ли код сиинтаксически корректным! Вот исходный пример, написанный на С [pre2]#include <stdio.h> int main( void ) { puts( "--- 1 ---" ); switch ( 0 ) if ( 1 ) case 0: puts( "0" ); else default: puts( "default" ); puts( "--- 2 ---" ); switch ( 0 ) while ( 0 ) if ( 1 ) case 0: puts( "0" ); else default: puts( "default" ); return ( 0 ); }[/pre2] Глядя на этот код, сразу же возникают сомнения, а будет ли он вообще компилироваться?! Но на самом деле, каким бы вычурным этот код не казался, но с синтаксической точки зрения он является корректным! Теперь нужно разобраться, как этот код должен работать, и какой результат следует ждать от выполнения программы. В первом случае в соответсвии с семантикой оператора switch управление перейдет по метке case 0 внутрь оператора if. В связи с чем будет напечатан строковый литерал "0". Теперь возникает вопрос, а куда дальше перейдет управление программы? Например, будет ли напечатан строковый литерал "default"? Здесь уже без непосредственного обращения к тексту описания стандарта С не обойтись! Что написано в стандарте по этому поводу? Читаем (6.8.4.1 The if statement #2): "If the first substatement is reached via a label, the second substatement is not executed." Итак, строковый литерал "default" не должен печататься. С первым случаем мы разобрались. Теперь рассмотрим второй случай. Во втором случае переход по метке сразу же перескакивает внутрь двух управляющих конструкций: while и if. С if мы разобрались: будет печататься строковый литерал "0", а предложение else будет пропущено. Теперь возникает вопрос, а как ведет себя while , если управление в него попало по метке внутрь тела. Опять-таки, без непосредственного обращения к стандарту не обойтись! Читаем (6.8.5 Iteratins statements #4): "The repetition occurs regardless of whether the loop body is entered from the iteration statement or by a jump" Следовательно после выполнения puts( "0" ) управление перейдет к while, в котором условие является ложным, а потому на этом выполнения данного кода закончится. Итак, в обоих случаях должен печататься строковый литерал "0". Второй случай можно сделать более интересным, если его слегка изменить. [pre2]#include <stdio.h> int main( void ) { int x = 0; puts( "--- 2 ---" ); switch ( 0 ) while ( x ) if ( !x ) case 0: puts( "0" ), ++x; else default: puts( "default" ), --x; return ( 0 ); }[/pre2] В этом случае сначала напечатается строковый литерал "0". При этом x станет равным 1. Управление перейдет на while. Условие будет истинным, а потому мы снова попадет внутрь тела while. На этот раз управление получит оператор else, и будет напечатан строковый литерал "default". x станет равным 0, и управление выйдет из тела while. Естественно любопытно, а почему появилось изначальное сообщение по указанной ссылке? Дело в том, что компилятор Intel дает другой результат!. В первом случае он печатает '0" "default" А во-втором случае, как и полагается, только "0" Хоть я и не согласен с автором изначального сообщения, что такой код можно использовать в своих программах, тем не менее он заслуживает благодарности за интересный пример!

Ответов - 0



полная версия страницы