Запретный плод GOTO сладок!

Доброго времени суток!

Какое Ваше отношение к оператору goto в языках С/С++? Скорее всего, когда Вы учились программировать, Вы его использовали. Потом Вы узнали, что это плохо, и Вы о нем позабыли. Хотя иногда при сложной обработке ошибок… нет-нет, там try … throw … catch. Или же для выхода из вложенных циклов … не-ет, там флаги и куча сложностей. Или когда вложенные switch … нет-нет-нет, там те же флаги.
И все-таки, иногда в ночной тиши Вы допускали в свое подсознание грешную мысль – «а почему бы не использовать вот тут goto? И программа вроде как стройней будет, и оптимально выходит. Да-а, было бы хорошо… Но нет – нельзя, забыли!».
А почему так оно?
Под катом – небольшое расследование и мое, основанное на многолетней практике и разных платформах, отношение к этому вопросу…
Особо любопытные и читатели Хабра уже возмутились — это кросспост с с Хабрахабра! Согласен — я там когда-то написал эту статью. Увидел, что она вызвала резонанс. Решил поделиться с читателями и этого ресурса.

Просьба к ярым противникам goto – не свергайте меня в геенну огненную минусовой кармы только из-за того, что я поднял эту тему и являюсь частичным сторонником goto!

Небольшой исторический экскурс

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

А все начиналось с комбинационных схем


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

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

Для решения этой сложнейшей задачи строился последовательный алгоритм для выполнения арифметических операций (в случае заданной точности вычислений в таком алгоритме каждое арифметическое действие можно выполнять за один такт).

Имея алгоритм, несложно построить комбинационную схему – схему, которая мгновенно (с точностью до срабатывания логических устройств и времени распространения сигналов) на выходе давала ответ.
Вопрос – тут нужны какие-нибудь переходы? Нет, их тут просто-напросто нет. Есть последовательное течение действий. Все эти действия можно реализовать в конечном счете за один такт (не спорю, это будет очень и очень громоздко, но задавшись разрядностью всех данных, такую схему Вам построит любой студент – и тем более синтезатор для VHDL или Verilog).

Но потом вмешались схемы с памятью


А потом чья-то умная голова додумалась до схемы с обратной связью – например, RS-триггер. И тогда появилось состояние схемы. А состояние – это ни что иное, как текущее значение всех элементов с памятью.

Появление таких элементов памяти позволило сделать революционный скачок вперед от жестко заданных устройств к микропрограммным автоматам. Упрощенно говоря, в микропрограммных автоматах есть память команд. Есть отдельное устройство, которое реализует текущую микропрограмму (сложение, вычитание или еще чего). А вот выбором «текущей» микропрограммы занимается отдельное устройство – пусть это будет «устройство выборки».

Вопрос – тут есть какие-нибудь переходы? Однозначно да! Более того, появляются переходы безусловные (адрес следующей команды не зависит от текущего состояния данных) и условные (адрес следующей команды зависит от состояния данных).

Можно ли без них обойтись? Да никак! Если не использовать переходы, то мы вернемся к комбинационной схеме без памяти.

В итоге мы пришли к ассемблеру

Апофеозом таких вычислительных устройств стали микро-, просто- и супер-компьютеры. Все они в основе имеют язык кодов, достаточно легко преобразуемый в Ассемблер с приблизительно совпадающим набором команд. Рассмотрим некий усредненный ассемблер микроконтроллеров (я знаком с ассемблером для ATmeg-и, PIC и AT90). Как у него построена работа переходов?

Переходы бывают безусловные (просто переход на следующий адрес, переход в подпрограмму, выход из нее) и условные (в зависимости от состояния флагов).

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

Итог

Какой итог можно подвести? На уровне микропроцессора операции перехода используются очень активно. Реальную программу, их не использующую, написать почти невозможно (может быть, ее можно сделать, но это будет супер-мега-извращение и точно уж не реальная программа!). С этим тоже спорить никто не будет.

Но почему же тогда в языках более высокого уровня – сконцентрируемся на С для микроконтроллеров — оператор goto вдруг впал в немилость?..

Немного об алгоритмах



А теперь посмотрим на хитровывернутый алгоритм. Представление не имею что это за бред – но его надо реализовать. Будем считать, это такое ТЗ.

Здесь A, B, C, D, E — это некоторые операции, а не вызов функции! Вполне возможно, что они используют массу локальных переменных. И вполне возможно, что они меняют их состояние. Т. е. в данном случае речь не идет о вызове функций — некоторые действия, не будем детализировать.

Вот как это выглядит с goto:


 
if (a)
{
	A;
	goto L3;
}
L1:
if (b)
{
L2:
	B;
L3:
	C;
	goto L1;
}
else if (!c)
{
	D;
	goto L2;
}
E;
 


Очень лаконично и читабельно. Но — нельзя! Попробуем без goto:


 
char bf1, bf2, bf3;

if (a)
{
	A;
	bf1 = 1;
}
else
	bf1 = 0;

bf2 = 0;
do
{
	do
	{
		if (bf3 || b)
			bf3 = 1;
		else
			bf3 = 0;
		if (bf3 || bf2)
			B;
		if (bf3 || bf1 || bf2)
		{
			C;
			bf1 = 0;
			bf2 = 1;
		}
		if (!bf3)
		{
			if (!c)
			{
				D;
				bf3 = 1;
			}
			else
			{
				bf3 = 0;
				bf2 = 0;
			}
		}
	}
	while (bf3);
}
while (bf2);

E; 


Вы что-нибудь поняли из логики работы второго листинга?..
Сравним оба листинга:

  • На первый листинг я потратил раз в 5 меньше времени, чем на второй.
  • Листинг с goto короче как минимум в 2 раза.
  • Листинг с goto поймет любой человек с самой минимальной подготовкой в С. Второй же я постарался сделать максимально доступным и очевидным – и все равно, в него надо долго вникать.
  • Сколько времени уйдет на отладку первого варианта и сколько на отладку второго?
  • И вообще, если считать нарисованный алгоритм постановкой задачи, то первый листинг правильный на 100%. Про второй я до сих пор не очень уверен… хотя бы в очередности проверки условий и флагов.
  • Сравните получившийся ассемблерный код первого и второго листинга.

Но зато во втором листинге нет goto!

Еще мне предлагали этот алгоритм реализовать приблизительно так:

 
if a
	A
	C

while b or not c 
	if not b
		D
	B
	C

E

 


Вроде бы красиво, да? Почему бы не сделать copy-past, почему бы не накрутить дополнительные проверки, почему бы… сделать все что угодно, кроме goto!!!

Ну да ладно, в жизни такие алгоритмы почти не встречаются. Лучше поговорим о жизни.

goto в реальных программах


Я за свой более чем 20-летний стаж прошел несколько аппаратных платформ и с десяток языков программирования, участвовал в написании крупного программного продукта ActiveHDL, делал коммерческую базу данных и много небольших программ для отладки оборудования, используемого в Олимпийских играх, а также делал устройства для этой самой Олимпиады (уже несколько Олимпиад, если быть точным). Короче, что-то я в программировании шарю. А, да, забыл – я закончил с почетным дипломом ХНУРЭ — то бишь, в теории я тоже секу.

Поэтому мои последующие размышления и ситуации… скажем так, я имею моральное право на них.

Неявное использование goto

В языке С есть много операторов, которые на самом деле являются банальным goto – условным или безусловным. Это все виды циклов for (…), while (…) {…}, do {…} while (…). Это анализ числовых переменных switch (…) {case … case …}. Это те же операторы прерывания/перехода в циклах break и continue. В конце концов, Это вызовы функций funct() и выход из них return.

Эти goto считаются «легальными» — чем же нелегален сам goto?

В чем обвиняют goto

Обвиняют его в том, что код становится нечитабельным, плохо оптимизируемым и могут появиться ошибки. Это про практические минусы. А теоретические – это просто плохо и неграмотно, и все тут!

Насчет нечитабельности кода и плохой оптимизируемости – еще раз взгляните на листинги выше.
Насчет вероятности появления ошибок – согласен, такой код воспринимается несколько сложнее из-за того, что мы привыкли читать листинг сверху вниз. Но и все! А что, другие средства С безопасные и не могут создать ошибок в коде? Да взять хотя бы преобразования типов, работа с указателями. Там напортачить — за нечего делать!

Вам не кажется, что нож – это очччень опасная вещь? Но почему-то на кухне мы им пользуемся. А 220 вольт – ужас как опасно! Но если пользоваться с умом – жить можно.

Тоже самое и goto. Пользоваться им надо с умом – и тогда код будет работать корректно.

А про теоретические доводы – это, уж простите меня, спор о вкусах. Вы пользуетесь Венгерской нотацией? Я – нет, терпеть ее не могу! Но я ж не говорю, что она плохая из-за этого! Лично я считаю, что переменная должна нести смысловую нагрузку – для чего она создана. Но я не буду запрещать пользоваться этим способом именования другим людям!

Или же есть эстеты, которые считают, что писать a = ++i неграмотно, надо писать i = i + 1; a = i. И что теперь, запретить и это тоже?

Обработка ошибок

Возьмем обработку входных пакетов с некоего внешнего устройства:



pack = receive_byte ();

switch (pack)
{
case ‘A’:
	for (f = 0; f < 10; ++f)
	{
		…
		if (timeout)
			goto Leave;
		…
	}
	break;

case ‘B’:
	…
}
Leave:
	…



Мы получили заголовок пакета. Проанализировали. Ага, пакет 'A' — значит, нам надо 10 раз сделать чего-то. Мы не забываем контролировать время работы этого участка — а вдруг вторая сторона зависла? Ага, таки зависла — сработало условие timeout — тогда выходим наружу — из цикла, из switch.

Тоже самое можно сделать и с помощью всяческих флагов — но работать это будет медленней, плюс ко всему займет такую дефицитную ячейку памяти. Оно того стоит?

Это случай простой. А ведь receive_byte () тоже может быть макрофункцией с обработкой таймаутов. И там тоже будут такие вот резкие выходы.

Это как раз тот самый случай, где я активно использую goto. Это мне позволило не попадать в «зависания» в случае проблем с внешними устройствами, UART, USB и т. п.

Выход из вложенного цикла наружу

Посмотрите на программу ниже:


char a, b, c;

for (a = 0; a < 10; ++a)
{
	for (b = 0; b < a; ++b)
	{
		if (!c)
			goto Leave;
	}
	for (b = 10; b < 15; ++b)
	{
		d ();
	}
}

Leave:
e ();


Что происходит – понятно? Есть вложенный цикл. Если наступило какое-то условие – покидаем все последующие обработки.

Данный код с флагами выглядит иначе:


char a, b, c, f1;

f1 = 1;
for (a = 0; a < 10 && f1; ++a)
{
	for (b = 0; b < a && f1; ++b)
	{
		if (!c)
			f1 = 0;
	}
	if (f1)
	{
		for (b = 10; b < 15; ++b)
		{
			d ();
		}
	}
}

e ();


Что произошло в данном случае? На каждой итерации мы теперь проверяем флаг. Не забываем его проверять и дальше. Это мелочи, если итераций немного и речь идет о «безразмерной» памяти у PC. А когда программа написана для микроконтроллера – это все уже становится существенно.

Кстати, в связи с этим в некоторых языках (если не ошибаюсь, в Java) есть возможность выйти из цикла по метке вида break Leave. Тот же goto, между прочим!

Точно такой же пример я могу привести и с обработкой в switch (…) { case …}. С этим я сталкиваюсь часто при обработке входящих пакетов неодинаковой структуры.

Автоматическое создание кода

Знакомы ли Вы с автоматным программированием? Или любым другим автоматизированным созданием кода? Скажем, создатели лексических обработчиков (без использования громоздкого boost::spirit). Все эти программы создают код, который можно использовать как «черный ящик» — Вам не важно, что там внутри; Вам важно, что он делает. А внутри там goto используется очень и очень часто…

Выход в одном месте

На С иногда приходится писать что-то вроде:


int f (…)
{
	…
	if (a)
	{
		c = 15;
		return 10;
	}
	…
	if (b)
	{
		c = 15;
		return 10;
	}
	…
	с = 10;
	return 5;
}


Этот код гораздо аккуратней будет выглядеть так:


int f (…)
{
	…
	if (a)
		goto Exit;
	…
	if (b)
		goto Exit;

	…
	с = 10;
	return 5;
Exit:
	c = 15;
	return 10;
}


Идея понятна? Иногда надо при выходе что-то сделать. Иногда много чего надо сделать. И тогда тут здорово помогает goto. Такие примеры у меня тоже имеются.
Вроде бы все перечислил, теперь можно подвести…

Итог


Это моя точка зрения! И она справедлива для меня. Может – и для Вас, но я не буду Вас заставлять ей следовать!

Так вот, для меня очевидно, что goto помогает оптимальней и качественней решить некоторые проблемы.
А бывает и наоборот – goto может породить массу проблем.

UPD: Начитавшись гору комментариев, я для себя выделил положительные стороны использования goto и отрицательные.

Плюсы использования goto:
  • самый оптимальный (с т. зр. листинга и результирующего кода) выход из нескольких вложенных циклов и switch… case
  • Си: наиболее экономичный (по листингу и результирующему коду) способ обработки ошибок
  • в отдельно взятых случаях самое оптимальное построение алгоритма
  • экономит память и такты при аккуратном использовании, что иной раз бывает первостепенно важным
Минусы использования goto:
  • непривычность кода
  • нарушение хода следования чтения листинга сверху вниз и стандартизированного обхода блоков в коде (в смысле, что возможен переход в центр блока, а также выход из его)
  • усложнение компилятору (а иногда и невозможность) процесса оптимизации кода
  • повышение вероятности создания трудноуловимых ошибок в коде

Кто еще подскажет плюсы/минусы? Впишу, если они будут оправданы.

Еще раз обращаю внимание: я не призываю тулить goto везде! НО в некоторых случаях он позволяет реализовать алгоритм куда эффективней всех остальных средств.
  • +8
  • 26 сентября 2012, 18:26
  • PICC

Комментарии (174)

RSS свернуть / развернуть
goto имеет строго ограниченную область видимости
нельзя делать

void foo()
{
...
goto m1;
...
}

int main(void)
{
...
foo();
...
m1:
...
...
...
return 100500;
}
0
не подумайте, что пытаюсь докопаться до Вас, но

int f (…)
{
        …
        if (a)
                goto Exit;
        …
        if (b)
                goto Exit;

        …
        с = 10;
        return 5;
Exit:
        c = 15;
        return 10;
}


лично для меня нисколько не аккуратно, а во втором варианте четко видно точки выхода из функции.
хотя у меня нет 20 лет стажа за спиной и могу ошибаться…
0
В духе:

int f() {
 if (a || b) {
    do_aorb(a, b);
    c = 10;
    return 10;
 } else {
   do_c();
   c = 15;
   return 5;
 }
}

если под «c» скрывается что-то более тяжёлое?
0
goto имеет строго ограниченную область видимости
нельзя делать

Это просто замечательно, что так нельзя делать. Не дай Бог встретить такое в коде…

Сам топик весьма провокационный, чувствую, будет веселое обсуждение :)
Как говорил VGA:
В теме и накаркали. Вон, смотри, «ЕДА» написано. …
0
Так нельзя делать потому, что межфункциональные переходы ломают логику структурного программирования — разделение кода не независимые функции (одна функция может вызвать другую, но при этом не зависит от ее внутреннего строения — только от интерфейса и результатов работы). Кроме того, вылазят чисто технические сложности типа чистки стека за чужой функцией, откуда внезапно прискакали по goto.
0
  • avatar
  • Vga
  • 27 сентября 2012, 01:48
Я знаю, почему так сделать нельзя. И я очень рад тому, что такие переходы невозможны :)
Вы правы, такие переходы полностью поломали бы логику структурного программирования.

А технические сложности (восстановление стека) теоретически можно преодолеть. Например так, как это сделано при обработке исключений. Только нафиг это нужно…
0
Ууу, обработка исключений — совершенно отдельная тема. Средства, применяемые там для раскрутки стека и прочих задач вполне мозговыносящи, повязаны на ОС (по крайней мере в языках, ориентированных на работу под ОС) и используют различные специализированные возможности. Применять их повсеместно было бы крайне неэффективно.

Но да, первопричина — несовместимость свободных прыжков с структурным программированием.
0
  • avatar
  • Vga
  • 27 сентября 2012, 02:35
Я знаю, почему так сделать нельзя.
Ну это был не (с)только ответ, но и дополнение-пояснение к твоим словам.
0
  • avatar
  • Vga
  • 27 сентября 2012, 02:37
Вот тут чувак хотел использовать ГОТО для перехода их одного обработчика прерывания в другое. Дальше тема вылилась в годный срач, потому как автор не желал слушать советы других людей о том, что так, мягко говорят, делать не нужно.
0
Бывает. Но это уже на совести автора.
0
  • avatar
  • Vga
  • 27 сентября 2012, 07:34
ой, модест — такой модест…
(кажется, у меня его ник становится именем-нарицательным...)
0
Расскажи подробнее.
0
  • avatar
  • Vga
  • 27 сентября 2012, 08:26
да, вообще эпичный персонаж.
начал с того, что он крутой жаба-программаер и решил заняться контроллерами. потом не смог завести (на авр!) то-ли усарт, то-ли еще что-то. (sic!) потом был веселый пост в стиле «гавно эти ваши авр — я ухожу от вас навсегда», при этом были загоны на другие архитектуры (в том числе стм32), вспоминая что их сложно купить. (ага, это стм-ки сложно купить!). потом его вроде как успокоили, и он продолжил свой эпичный труд.
ну и теперь периодически радует подобными темами, при этом поучая народ как надо писать и доставляя немало лулзов. при этом, похоже, даже букварь K&R не удосужился хоть пролистать…
да, попытка заюзать на авр динамическое распределение (есть там де-то вопрос) памяти тоже повеселила.
0
вспоминая что их сложно купить
Ну да. У нас в городе, например, их не купить)
даже букварь K&R не удосужился хоть пролистать…
Признаться, я тоже.
да, попытка заюзать на авр динамическое распределение (есть там де-то вопрос) памяти тоже повеселила.
Why not?

Но судя по тому, что я вижу в ветке — да, все это применимо к нему в черном свете)
0
  • avatar
  • Vga
  • 27 сентября 2012, 08:44
Ну да. У нас в городе, например, их не купить)
ну, почта вроде более-менее работает и транспортных контор достаточно. это ведь не ренесас какой-нить или латтисы, которые надо из фарнела, например, тащить…
Признаться, я тоже.
тебе простительно. ты таких громких заявлений не делаешь. ;)
Why not?
потому, что не на том уровне с жабопривычками. и, простите, похоже, теми еще навыками…

да там еще и в вальхаллу, кажется, что-то было снесено.
мое имхо — ярчайший образчик воинствующего невежества.
0
ну, почта вроде более-менее работает и транспортных контор достаточно.
Ну, по сравнению с AVR (теми полутора вариантами, что есть в местном магазине) — сложнее же) Да и чтобы обзавестись STM32F100 и STM8S003 по правильной цене — пришлось напрягать свою дипломатию и тех, кому повезло обитать в Москве)
тебе простительно. ты таких громких заявлений не делаешь. ;)
Еще как делаю. Только аргументированно, поскольку я читал не так уж мало других материалов.
потому, что не на том уровне с жабопривычками. и, простите, похоже, теми еще навыками…
Вот на это я и намекаю — все дело в нюансах :)
0
  • avatar
  • Vga
  • 27 сентября 2012, 08:59
Ну, по сравнению с AVR (теми полутора вариантами, что есть в местном магазине) — сложнее же) Да и чтобы обзавестись STM32F100 и STM8S003 по правильной цене — пришлось напрягать свою дипломатию и тех, кому повезло обитать в Москве)
хех. да разве это сложности? вот как я покупал свою первую VS1001 еще, кажется, и де-то на дальнем востоке (одна контора занималась), да с переводом денег, да с новыми границами. в начале нулевых. вот это был квест.
а щас — просто рай на земле. ;)

Еще как делаю. Только аргументированно, поскольку я читал не так уж мало других материалов.
Вот оно! тем более не впадаешь в истерику, как только что-то не срастается.

лан. хорош модеста обсуждать.
0
лан. хорош модеста обсуждать.
Ну надо же местных героев знать) Вдруг он в сообщество придет.
0
  • avatar
  • Vga
  • 27 сентября 2012, 09:15
И еще по поводу ссылки.
Автор не особо адекватен, да. Но и половина отвечающих тоже. Некоторые о гото и вовсе знают только то, что им вбили — «зло, не использовать!».
А еще я крайне удивлен, что межпроцедурный прыжок скомпилировался без ошибок. По идее, должен был вывалиться мат о том, что метка не найдена. Или это какая-то ошибка, или авторы компилятора накосячили.
0
  • avatar
  • Vga
  • 27 сентября 2012, 08:15
Или это какая-то ошибка, или авторы компилятора накосячили.
По видимому, первое. Modest, как я понял, использует AVR Toolchain (тобишь AVR-GCC). На мои попытки сделать goto из одной функции в другую (а также из одного ISR в другой) компилятор, как и положено, отреагировал матом.
Так что скорее всего ему только показалось, что оно компилируется. Или он вообще не перекомпилировал этот файл, или обреботчик с goto был отключен дефайном (он как раз в один из ifdef'ов обернут).
0
  • avatar
  • Vga
  • 27 сентября 2012, 10:01
Сам топик весьма провокационный, чувствую, будет веселое обсуждение :)
Да уж, этот топик вызывает бурные дискуссии, и это класс :-)! Лично для себя по итогам дискуссии уже кое-какие выводы почерпнул
0
Лично для себя по итогам дискуссии уже кое-какие выводы почерпнул
Это главное. Хотя, бурные дискуссии ненамного менее главные)
0
  • avatar
  • Vga
  • 27 сентября 2012, 17:58
На языках мало-мальски высокого уровня — нельзя, конечно. Хотя на Ассемблере это иногда используется (защита от трассировки и анализа программы, например)
0
В ассемблере нету функций. Есть только непрерывное АП с кодом (и данными, у фон-неймановских архитектур). Прыгай куда хочешь.
Такие прыжки, впрочем, скорее используются для оптимизации. Обфусковывается код несколько другими средствами. Да и от трассировки больше спасают противоотладочные меры. Хотя StarForce и это не спасает.
0
  • avatar
  • Vga
  • 27 сентября 2012, 18:00
Как правило goto требуется для реализации монструозных алгоритмов, которые сами по-себе зло. Если переработать алгоритми разбить код на функции, то и goto не потребуется.
0
  • avatar
  • m0xf
  • 26 сентября 2012, 19:31
Но при этом оно может не влезть в память или провалить по быстродействию.
0
в «тяжелом программировании» такие оптимизации не должны являться узким местом. если это случилось — пора переписывать проект с нуля.
в эмбедде же вероятно использование goto вполне оправдано — там вообще оправдана практически любая грамотная(!) экономия. впрочем, в большинстве случаев можно и без goto обойтись, практически или почти ничего не теряя. другой вопрос, что не все умеют это красиво делать(ага, та самая шняга про то, что большинство электронщиков программисты так себе и наоборот — мощные программеры зачастую хреново рубят в электронике). иными словами, гуру в обеих областях не исключение из правил, но тем не менее достаточная редкость.
0
в большинстве случаев можно и без goto обойтись, практически или почти ничего не теряя
А как же тогда обработка исключений во вложенном алгоритме? Или же вложенные циклы? Как раз те исключения, которые подтверждают правило :-)
0
Может просто не стоит писать вложенные алгоритмы или циклы?
0
А что такое «вложенный алгоритм»?.. Сам вот задумался…
0
Я бы еще вас в догонку спросил, что именно вы понимаете под словосочетанием «обработка исключений» )))
0
Говорят, что в Java нет goto. Что теперь делать?

А вообще, С. Макконнелл «Совершенный код», глава 17, раздел 17.3 Оператор goto, стр. 389. Читать всем.
+1
  • avatar
  • uni
  • 26 сентября 2012, 19:58
Говорят, что в Java нет goto.

Но ключевое слово «goto» они, на всякий случай, зарезервировали :)
0
А тем у кого нет данной книги и гугл в бане?
0
Тоже иногда тянет использовать goto, но всегда обходился без него.
while(1){
if(a){A;C;}
else if(b){B;C;}
else if(C){E;return;}
else {D;B;C;}
}
A,B,C,D — вызовы функций, потери при вызове, зато экономия памяти на локальных переменных.
0
Упс проглядел пару моментов. Первый if(a) перед while. Оператор goto не рекомендован к использованию из-за ориентации на процедурный подход. В качестве параметра фунциям можно передавать указатель на данные. Что будет с goto, если потребуется изменить алгоритм? Скажем так if(!c){C;D;}?
0
От программирования я далек, но уже за труд можно плюсануть =)
0
Я бы разделил вообще код на вычисляющий и управляющий. Это разные вещи и требования для их реализаций тоже разные. Вычисляющий обычно построен по математической формуле, управляющий — по стандартизованному протоколу. Хотя и в вычисляющем можно встретить управляющий вариант, например, обработка ошибки деления на ноль, которая тут сверху в примерах неявно указана.

Самый простой способ посмотреть как используется goto в проектах для микроконтроллеров — это взять такой проект и пройтись по нему поиском. Возьмите, к примеру, freemodbus, поищите там во всех файлах строку «goto » и вы обнаружите все возможные варианты, когда goto мог бы пригодиться. Все эти конструкции там управляющие и, как правило, сводятся лишь к одному: общая реакция на множество исключительных ситуаций, причем большинство этих goto можно было бы заменить на return КОД_ОШИБКИ, но в целях отладки нужна ещё одна команда — команда отладки, ведения лога.

Вот типовой код (lcp.c):


/*
 * lcp_nakci - Peer has sent a NAK for some of our CIs.
 * This should not modify any state if the Nak is bad
 * or if LCP is in the OPENED state.
 *
 * Returns:
 *	0 - Nak was bad.
 *	1 - Nak was good.
 */
static int lcp_nakci(fsm *f, u_char *p, int len)
{
	lcp_options *go = &lcp_gotoptions[f->unit];
	lcp_options *wo = &lcp_wantoptions[f->unit];
	u_char citype, cichar, *next;
	u_short cishort;
	u32_t cilong;
	lcp_options no;		/* options we've seen Naks for */
	lcp_options try;		/* options to request next time */
	int looped_back = 0;
	int cilen;
	
	BZERO(&no, sizeof(no));
	try = *go;

        // ...

	/*
	* There may be remaining CIs, if the peer is requesting negotiation
	* on an option that we didn't include in our request packet.
	* If we see an option that we requested, or one we've already seen
	* in this packet, then this packet is bad.
	* If we wanted to respond by starting to negotiate on the requested
	* option(s), we could, but we don't, because except for the
	* authentication type and quality protocol, if we are not negotiating
	* an option, it is because we were told not to.
	* For the authentication type, the Nak from the peer means
	* `let me authenticate myself with you' which is a bit pointless.
	* For the quality protocol, the Nak means `ask me to send you quality
	* reports', but if we didn't ask for them, we don't want them.
	* An option we don't recognize represents the peer asking to
	* negotiate some option we don't support, so ignore it.
	*/
	while (len > CILEN_VOID) {
		GETCHAR(citype, p);
		GETCHAR(cilen, p);
		if (cilen < CILEN_VOID || (len -= cilen) < 0)
			goto bad;
		next = p + cilen - 2;
		
		switch (citype) {
		case CI_MRU:
			if ((go->neg_mru && go->mru != PPP_DEFMRU)
					|| no.neg_mru || cilen != CILEN_SHORT)
				goto bad;
			GETSHORT(cishort, p);
			if (cishort < PPP_DEFMRU)
				try.mru = cishort;
			break;
		case CI_ASYNCMAP:
			if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl)
					|| no.neg_asyncmap || cilen != CILEN_LONG)
				goto bad;
			break;
		case CI_AUTHTYPE:
			if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap)
				goto bad;
			break;
		case CI_MAGICNUMBER:
			if (go->neg_magicnumber || no.neg_magicnumber ||
					cilen != CILEN_LONG)
				goto bad;
			break;
		case CI_PCOMPRESSION:
			if (go->neg_pcompression || no.neg_pcompression
					|| cilen != CILEN_VOID)
				goto bad;
			break;
		case CI_ACCOMPRESSION:
			if (go->neg_accompression || no.neg_accompression
					|| cilen != CILEN_VOID)
				goto bad;
			break;
		case CI_QUALITY:
			if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR)
				goto bad;
			break;
		}
		p = next;
	}
	
	/* If there is still anything left, this packet is bad. */
	if (len != 0)
		goto bad;
	
	/*
	* OK, the Nak is good.  Now we can update state.
	*/
	if (f->state != OPENED) {
		if (looped_back) {
			if (++try.numloops >= lcp_loopbackfail) {
				LCPDEBUG((LOG_NOTICE, "Serial line is looped back.\n"));
				lcp_close(f->unit, "Loopback detected");
			}
		} 
		else
			try.numloops = 0;
		*go = try;
	}
	
	return 1;
	
bad:
	LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!\n"));
	return 0;
}
0
  • avatar
  • uni
  • 26 сентября 2012, 21:18
По поводу картинки сверху:



\freemodbus\demo\ATSAM3S\libraries\drivers\utility\retarget.c:


void _sys_exit(int return_code) {
    label:  goto label;  /* endless loop */
}
0
  • avatar
  • uni
  • 26 сентября 2012, 21:26
goto bad;
заменим на
return LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!\n")), 0;
более того можно сообщение детализованнее выдать — для лога полезнее.
0
А потом править эту строчку в десятке мест, не говоря уже о повышенном расходе памяти программ (ну разве что компилер достаточно умен, чтобы привести код к первому варианту с goto)? Такая штука хорошо заменяется на обработку исключений, но на МК с ней несколько туговато.
И вообще, если бы goto был плох всегда, в С/С++ его бы просто не включили.
И кстати, goto такой не один. Тот же Макконнел наравне с ним приводит рекурсию.
0
  • avatar
  • Vga
  • 27 сентября 2012, 02:03
Что править-то? сообжение сразу индивидуальным создаётся, а макрос на то и применяется.
Компилятор достаточно умен.
Перерасход памяти только в отладке, когда он необходим.
Я не говорю что goto всегда плох. Я лишь отметил что приведённый пример не вполне удачен. В качестве примера полезности goto лучше приводить пример инициализации большого количества разношерстных объектов. Иными словами код драйверов *nix систем.
Написать
if(...) {
 LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!\n"));
 return 0;
}
Является так же более предпочтительным, ввиду наличия скобок. Кто не в курсе, даже одиночные инструкции рекомендуется брать в скобки для упрощения анализа кода.
0
сообжение сразу индивидуальным создаётся
Не обязательно — здесь на каждый вариант срабатывания одно и то же сообщение. Не стоит забывать, что каждое лишнее сообщение — это лишний расход флеш-памяти МК.
И здесь goto как раз уместен. Твои варианты больше похожи на костыли исключительно для избавления от goto.
Кстати, множественные выходы из функции тоже не так уж хороши.
0
  • avatar
  • Vga
  • 27 сентября 2012, 14:50
Вы добавляете оладочную информацию или что? Как, имея в логе один текст, определить которое из условий сработало?
Да, я приводил пример кода, когда исключив goto из кода, можно добится лучшего понимания, как кода, так и сформированных кодом логов работы.
P.S.: по поводу варианта с фигурными скобками = костыль, достаточно интересное мнение.
0
Вы добавляете оладочную информацию или что?
Во всем нужна мера. Одна моя игрушка выплевывает в лог индивидуальную строку на каждую ошибку. Так вот, отключение лога сокращает программу на 10-15 килобайт (при размере около сотни), хотя этих строк не так уж много.
Как, имея в логе один текст, определить которое из условий сработало?
Зачастую достаточно знать того, что пришел плохой нак.
P.S.: по поводу варианта с фигурными скобками = костыль, достаточно интересное мнение.
Костыль — это копипаста одного и того же кода по всем if'ам. Неважно, со скобками или без.
0
  • avatar
  • Vga
  • 27 сентября 2012, 15:10
if(!*str) return 0;
А я то думал что это защита, а это оказывается костыль! И ведь на всё 100% подходит под определение :D
0
Одна команда еще не копипаст. Алсо без контекста про эту строчку вообще ничего нельзя сказать — ни «костыль», ни «не костыль».
0
  • avatar
  • Vga
  • 27 сентября 2012, 18:02
Очень лаконично и читабельно
Ни разу не читабельно. Сразу и не понять что данный «кусок» кода делает и что в нем могут быть циклы (даже вечные).
Сколько времени уйдет на отладку первого варианта и сколько на отладку второго?
… ну второй вариант первого алгоритма следовало бы и убрать, а то выглядит как провокация.

Вот к примеру как можно организовать первый алгоритм:

void monitoring_b_state(void)
{
    while(b)
    {
        B;
        C;
    }
}

...
...
...

if(a)
{
    A;
    C;
}
monitoring_b_state();
while(!c)
{
    D;
    B;
    C;
    monitoring_b_state();
}
E;


Я лично ничего против goto не имею, но как по мне его использование есть признак лени непроработанного алгоритма, а то что код без использования goto выглядит (для кого-то) более криво, ну что поделать — такое ТЗ.
0
+3
  • avatar
  • John
  • 26 сентября 2012, 21:47
Начинающих программистов за goto надо бить по сусалам. Потому что 90% случаев использования goto — из-за лени логически организовать алгоритм, и только 10% это действительно оптимизация. Вот когда подрастут, морда им отрастет, тогда они начнут придумывать собственные хаки. Опытные программеры избиения не боятся, и используют goto правильно. А если отбросить политику, оператор как оператор.
+2
Собственно да, именно. С goto очень легко сделать спагетти-код, и новичок так и сделает. Поэтому новичку goto преподносят как нечто плохое с расчетом на то, что когда вырастет — сам поймет в каких случаях оно хорошо, а в каких плохо и будет применять уже с умом.
Вот только некоторые этого так и не понимают и сохраняют неприязнь к goto на всю жизнь. Да еще и на других катят «фу, goto, убейся апстену!» не разбираясь, насколько он оправдан.
0
  • avatar
  • Vga
  • 27 сентября 2012, 02:08
И кстати, еще один момент запрета goto новичкам — они привыкнут решать задачи более высокоуровневыми средствами и вспомнят о goto только тогда, когда застрянут в ситуации, где все остальные методы приводят к крайне корявым результатам.
+1
  • avatar
  • Vga
  • 27 сентября 2012, 02:09
все остальные методы

Здесь точно обсуждают С?
0
А что?
0
То, что в C всегда миллион возможных вариантов реализации алгоритма.
0
Иногда бывает так, что из всего миллиона только goto позволяет решить задачу красиво или эффективно (зависит от требований). Редко — но бывает.
0
Обсуждение GOTO не привязано к конкретному языку.
0
Какое Ваше отношение к оператору goto в языках С/С++?
0
«Сон разума рождает чудовищ»…

Если бы вы не поленились разработать нормальный, структурированный алгоритм, (вместо того клубка лапши, что вы пытаетесь повесить на уши читающим это), то прекрасно обошлись бы без всяких ГОТО.

Мало того, его вообще некуда там было бы приткнуть.

Вы же навертели спагетти уже в самом алгоритме, а потом начали говорить, как хорошо он реализуется с ГОТО…

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

Почитайте лучше статьи и книги по правильному структурному программированию. Возможно, вы их и читали, да не поняли, в свое время.
Может, следующая попытка будет более удачной…
+3
  • avatar
  • SWG
  • 26 сентября 2012, 22:29
автор говорит о том, что гоуту тоже есть место. но уместно.
0
+1. Еще ни разу не использовал goto и не пойму нафиг он нужен.
+1
  • avatar
  • a9d
  • 27 сентября 2012, 00:18
Иногда он в таком виде и есть. Какая-нить блок-схема обработки некоего протокола, скажем (ну, например, схема конечного автомата TWI из даташита на атмеги).
Обойтись без goto можно всегда, но не всегда это оправдано.
0
  • avatar
  • Vga
  • 27 сентября 2012, 02:11
тоже хотел что-то в этом духе сказать, но скажу короче
сферический быдлоалгоритм в вакууме бесполезен, как быдлозадачи в быдловузах

если бы автор показал реальную реализацию в какой-то своей задаче с гото и без, сравнил бы потребление памяти и времени, то было бы годно

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

с другой стороны — если есть оператор, значит его зачем-то туда воткнули
надо только разобраться и использовать инструмент по назначению, так что я не против

когда писал на асме, то не использовал переходы по меткам особо и очень сильно ломал мозги, чтобы реализовать алгоритм
препод нихера не сказал, как пользоваться инструментом
но что взять с быдловуза?
-1
если бы автор показал реальную реализацию в какой-то своей задаче с гото и без, сравнил бы потребление памяти и времени, то было бы годно
Я помню один гото-срач на дельфимастере. Там один хороший программист выложил свой модуль преобразования в Base64 на дельфи, с основной фишкой — высокой скоростью преобразования. Впрочем, точнее, срача почти не было, потому как на претензию «фу, гото» он ответил «я знаю. не нравится — напиши свой, без гото и быстрее или заткнись».
кстати почему всегда говорят про начинающих и гото?
Я это уже объяснял — раз, два.
сферический быдлоалгоритм в вакууме бесполезен, как быдлозадачи в быдловузах
Так обычно говорят ламеры, которые так и не поняли, чему их учили. Хотя бывают и плохие примеры — макконнел, например, пеняет на традиционную для учебников задачу на рекурсию — числа фибоначчи и факториалы.
0
  • avatar
  • Vga
  • 27 сентября 2012, 05:47
Я знаю другой пример на Дельфи: TRegExpr class library, Delphi Regular Expressions, Copyright (C) 1999-2004 Andrey V. Sorokin, St.Petersburg, Russia. 4000 строк кода, нафаршированных нетривиальными циклами. Ни одного goto. Разбор регулярок — это не хухры мухры. Хотел бы я посмотреть на того, кто бы стал там что-то переделывать с goto ;).
0
  • avatar
  • uni
  • 27 сентября 2012, 06:50
Я не призываю все на goto переводить. Но во многих случаях он оптимален с точки зрения производительности (кстати, регулярки — отнюдь не пример быстрого кода, вполне возможно, что их можно существенно оптимизировать).
И кстати, как уже упоминалось, генераторы парсеров goto активно используют в сгенерированном коде. Да и прочие кодогенераторы.
0
  • avatar
  • Vga
  • 27 сентября 2012, 07:38
И да, найти многострок без единого goto вообще не проблема. Далеко даже ходить не надо — в моем собстивенном коде он крайне редкий гость. Это был пример случая, где goto нужен для оптимального по быстродействию (да и читабельности) кода.
Нетривиальные циклы — врядли образец читабельного кода.
0
  • avatar
  • Vga
  • 27 сентября 2012, 07:43
Я помню один гото-срач на дельфимастере.
На DK, помню, разбирался алгоритм (тоже в контексте goto-срача), где все гуру согласились, что написать его короче и быстрее другими способами невозможно. Алгоритм короткий, но вот какой — хоть убей не помню…
0
на спектруме в бейсике он был за неимением реализации функций
вот и пример. есть функции в спековском васике. то ты просто недочитал.
0
есть функции в спековском васике. то ты просто недочитал.
Где, какие?
0
  • avatar
  • Vga
  • 27 сентября 2012, 07:38
еще скажи, что уже упомянутый gosub — это не аналог функции/процедуры.
0
Нет, не аналог. Это GO TO как он есть. Кстати, в некоторых ассемблерах даже нету CALL (наиболее прямой аналог GO SUB) — он реализуется через JMP.
Функции в васике есть, но только встроенные — SIN, COS, etc. А то, что реализуется при помощи GO SUB — subroutine, подпрограмма. Именно отсутствием составного оператора и функций и объясняется необходимость команды GO TO в старых (не структурных) бейсиках.
0
  • avatar
  • Vga
  • 27 сентября 2012, 08:29
гхм. объясни принципиальное отличие подпрограммы от процедуры.
0
Процедура — это функция юез возвращаемого значения. Так что дальше о функциях.
А функция — это не только подпрограмма, но и интерфейс, область видимости и время жизни. И основополагающий элемент структурного программирования.
А бейсик в своих инкарнациях на корвете/микроше/радио/спекки — ни разу не структурный. Он имеет ту же парадигму, что и большинство ассемблеров (к сожалению, не знаю ее точного названия — вики листать лень и вики-трипа опасаюсь).
0
  • avatar
  • Vga
  • 27 сентября 2012, 08:47
ну, родовые болячки васика понятны. и что такое функция — тоже.
но все-же. в чем принципиальное отличие процедуры от васиковой подпрограммы? я ведь именно об этом спрашивал.

ОЙ. я с какого-то в том комменте смешал процедуры, функции…
смешались в кучу кони, люди… надо больше спать.
0
Я же сказал — в том, что она не ограничивает область видимости и не имеет локальных переменных и аргументов. А так через подпрограммы функции и реализуются — ведь в ассемблере есть только они. Но тем не менее — подпрограмма не функция.
Вот разница между процедурой и функцией менее очевидна.
0
  • avatar
  • Vga
  • 27 сентября 2012, 09:02
Вот такие например:
FN A.… — Function with a numeric value;
FN A$… — Function with a text value;
DEF FN A$ (A, A $,...) — parameters in the function definition.
0
А, да. Я их даже использовал вроде.
Но стоит отметить, это не функция в терминах структурных языков. Это математическая функция. Различия еще больше, чем между процедурой и подпрограммой.
0
  • avatar
  • Vga
  • 27 сентября 2012, 09:28
Анекдот по поводу goto в ассемблере:

Группа системных и группа прикладных программистов едет на семинар на поезде. Прикладные программисты берут каждый по билету, системные — один на всех.

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

Едут обратно. Прикладники покупают один билет, системщики не покупают ни одного.

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

Мораль, на все, что подходит для системного программирования, подходит для прикладного!
+3
В ядре linux активно пользуются goto =).
0
Я как раз об этом ;).
0
Со всей ответственностью заявляю – без операций перехода в ассемблере обойтись невозможно! Любая программа на ассемблере просто таки пестрит ими! Впрочем, тут со мной никто спорить, я думаю, не будет.
Я буду. Ассемблеры бывают разные и вполне можно построить ассемблер, который без явных переходов обходится. Частично к таким относится, например, ассемблер ARM — там без переходов не удастся реализовать только циклы, которые нельзя развернуть. Хотя такой код будет неоптимален.
В реальности таких ассемблеров нет или практически нет по той простой причине, что реализовать более высокоуровневую парадигму программирования в железе накладно и не нужно — дешевле спихнуть ее на компилятор. А кто полез в ассемблер с его goto — ССЗБ.
0
  • avatar
  • Vga
  • 27 сентября 2012, 02:18
Ну и резюмирую свою точку зрения, уже раскиданную по нескольким комментариям.
В целом, я поддерживаю автора — goto при применении с умом весьма полезен. Собственно, поэтому его в С и добавили — «Язык С — как нож мясника: прост, остр и исключительно эффективен в умелых руках. Как и всякое острое орудие, С может принести увечья тем, кто не умеет им пользоваться.», и goto в полной мере соответсвует этому принципу.
Основные причины нелюбви к goto — новички, если им не запретить goto, практически гарантированно будут им пользоваться и порождать спагетти-код, при этом без goto вполне можно обойтись во всех ситуациях (другой вопрос — насколько оптимально это будет, из всех циклов вон тоже абсолютно достаточно только одного типа, причем любого). Кроме того, если запретить новичкам goto, они привыкнут решать типичные задачи более высокоуровневыми средствами, поймут и освоят их и уже не будут лепить goto без надобности.
Имея же достаточный опыт — goto вполне можно применять при необходимости. Впрочем, при достаточном опыте необходимость и даже желание применить goto возникают весьма редко.
+1
  • avatar
  • Vga
  • 27 сентября 2012, 02:32
Я не противник goto. Но у него, как и у всех общих решений, есть некий минус — оно несет в себе меньше информации о происходящем, чем более специфические средства.

Если я пишу continue; — сразу видно, что переход идёт к началу текущего цикла, то есть суть тут не в «прееходе» а в «начале новой итерации». В случае использования goto мне надо смотреть, где находится метка, а continue воспринимается как иероглиф, с одного взгляда. Точно такие же преимущества у циклов — от for до until — они яснее выражают семантику алгоритма.

А вот в случаях выхода из вложенных циклов (или циклов внутри switch) я целиком на вашей стороне — здесь попытки придумать специфические конструкции вроде break(2) — то есть прервать два уровня — или наворотить флаги — несут МЕНЬШЕ информации, чем какой-нибудь goto InvalidInput; и ухудшают понимание алгоритма. С другой стороны если у нас break — это «ограниченный goto», переходящий на метку, но работающий только там, где break имеет смысл — получается надёжнее — нет шансов случайно выскочить на неправильную метку (ценойболее сложного синтаксиса языка). Но в сях пытаться обходиться без goto любой ценой — чушь несусветная, конечно.
0
  • avatar
  • ali
  • 27 сентября 2012, 03:38
Я думаю, что было бы правильным всем тутошним спецам по goto предъявить свой код, как пример того, что вы осмысленно пользовались goto в реальном исходнике. Начну с себя. Единственный исходник с goto у меня оказался в тетрадке. Оказывается, я очень не плохо владел goto на Sinclair Basic'е, когда в далёком 1995 г. наваял игру Сокобан в псевдографике. Вот подтверждение кода — первая страничка листинга (она была и на кассете, но та не сохранилась для истории):

Сокобан, 14.06.1995 г.

Примечание: В первых строчках кода здесь идёт загрузка данных лабиринта и его отрисовка на экране.

В то время кроме goto я также умело пользовался gosub :). Так что могу сразу сказать, что как новичок, я прошёл все стадии goto-изации и очень долго от неё отходил. Это было ещё не самое страшное. Поскольку нормальных книг по программированию на ZX Spectrum не было, для меня было большим откровением узнать, что идентификаторы могут иметь длинные имена (!). Судя по примерам вверху, некоторые до сих пор не в теме.

Когда же до меня начал доходить истинный смысл ООП, то я стал везде, где это возможно, приводить листинги в человеческий вид.
0
  • avatar
  • uni
  • 27 сентября 2012, 04:57
Да, я тоже начинал с васика, на корвете и позже — спекки.
Без GOTO в них делать нечего — по парадигме они на уровне асма (GOTO — JMP, GOSUB — CALL). Тамошние подпрограммы — именно подпрограммы, хотя некоторые путают их с функциями и процедурами структурных языков.

Goto в Delphi я использую иногда, но очень редко.

Поскольку нормальных книг по программированию на ZX Spectrum не было, для меня было большим откровением узнать, что идентификаторы могут иметь длинные имена (!)
Эээ, а разве в zx-васик можно было юзать имена длиннее двух знаков? У меня, кстати, был неплохой мануал по васику от клона спекки, и ЕМНИП там говорилось именно про то, что идентификаторы могут быть только одно-двухсимвольными. Хотя в бейсике спектрума-128 это могло и измениться.
0
  • avatar
  • Vga
  • 27 сентября 2012, 05:03
Вот что я имел в виду: ZXBasic Manual CHAPTER 11

Here is a program to toss coins and count the numbers of heads and tails.

        	10 LET heads=0: LET tails=0
        	20 LET coin=INT (RND*2)
        	30 IF coin=0 THEN LET heads=heads+1
        	40 IF coin=1 THEN LET tails=tails+1
        	50 PRINT heads;",";tails,
        	60 IF tails<>0 THEN PRINT heads/tails;
        	70 PRINT: GO TO 20

CHAPTER 7:

Here are some examples of the names of variables that are allowed:
 x
 t42 
 this name is so long that I shall never be able to type it out again 
  without making a mistake 
 now we are six       		[these last two names are considered the same, and 
 nOWWeaReSiX            		    refer to the same variable]


Впервые с этим я столкнулся, когда разбирал загрузчики игры, которые были написаны на бейсике. Меня удивило, что многие переменные там имеют осмысленные идентификаторы. Строковые переменные и индексы должны быть из одной буквы, а числовые могут быть почти произвольными.
0
  • avatar
  • uni
  • 27 сентября 2012, 05:34
We really ought to tell you what you can and cannot use as the names of variables. As we have already said, the name of a string variable has to be a single letter followed by $; and the name of the control variable of a FOR-NEXT loop must be a single letter; but the names of ordinary numeric variables are much freer. They can use any letters or digits as long as the first one is a letter. You can put spaces in as well to make it easier to read, but they won't count as part of the name. Also, it doesn't make any difference to the name whether you type it in capitals or lower case letters.
Забавно. Значит, ограничение «1 или 2 символа» — это из корвет-бейсика. Вообще, найти в этом руководстве указания по именованию переменных не так просто. А вот типичные руководства по современным языкам обычно сразу сообщают, что «идентификатор может состоять из любого числа букв латинского (иногда — любого) алфавита, цифр и знака подчеркивания и не должен начинаться с цифры».
0
  • avatar
  • Vga
  • 27 сентября 2012, 05:55
Забавно. Значит, ограничение «1 или 2 символа» — это из корвет-бейсика.
Некоторые разновидности не только Бэйсиков, по и Ассемблеров, разрешали использовать достаточно длинные имена меток (лишь бы пробелов не было). Но при этом компилятор учитывал в них только первые 2 (реже — 3) символа, которые не должны были совпадать у разных меток. Это еше с «больших» ЭВМ пошло.

Также в некоторых разновидностях Бэйсиков разрешалось не нумеровать строки, кроме тех, на которые были переходы.

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

В самой большой из написанных мной программ на Бэйсике (Бэйсик — микро, от Микро-80), было несколько тысяч строк (по нумерации — более 20 тысяч, с исходным шагом 10, но были и куски с шагом 1-2), где-то тысячи 3-4.
Начинал ее как дизассемблер, потоянно наращивал, превратив потом в мощный инструмент работы с кодом. Можно было искать куски кода, заменять, сдвигать по адресам, редактировать листинг, выделять подпрограммы, отслеживать переходы, проставляя метки, и многое другое. После переделки снова получал заново откомпилированный, работающий машинный код.
Работа шла через меню, в котором было десятка два пунктов. Этой программой я году в 87 полностью дизассемблировал и переделал под свой комп операционку CP/M-80…

Много поработав в свое время на асме и Бэйсиках, я хорошо знаю, что такое GOTO, его удобства и недостатки. Но проникшись потом идеями структурного программирования, особенно после перехода на Турбо Паскаль (году в 95), перестал испытывать в нем необходимость. Ну, кроме ассемблера микроконтроллеров, где без него сложно.

В Паскалях же и Дельфи — GOTO мне абсолютно не нужен. Уже и забыл о нем…
0
  • avatar
  • SWG
  • 27 сентября 2012, 08:04
Разновидностей бейсика ооооооооооооочень много. Я говорил именно про интерпретирующие бейсики на старых компьютерах, вроде корвета, где ограничения на длину имен переменных вводились в целях оптимальности (программу нужно было хранить в ОЗУ, которой было всего от 16 до 48кб, да и сравнивать длинные идентификаторы долго). А что посовременней уже таких ограничений не имеет, да и парадигму обычно имеет как минимум структурную. Вот я и был несколько удивлен, что один из таких бейсиков на самом деле позволяет произвольные имена переменных.
было несколько тысяч строк (по нумерации — более 20 тысяч, с исходным шагом 10, но были и куски с шагом 1-2), где-то тысячи 3-4.
Как будто это много. Хотя да, на бейсике тех времен контролировать программу такого размера уже сложно.
Много поработав в свое время на асме и Бэйсиках, я хорошо знаю, что такое GOTO, его удобства и недостатки.
В Паскалях же и Дельфи — GOTO мне абсолютно не нужен. Уже и забыл о нем…
Разве я спорю? Я уже говорил, что на структурных языках goto реально нужен крайне редко. В принципе, можно без него обойтись полностью, но есть редкие случаи, где он резко улучшает производительность и/или читаемость кода. Так-то я тоже уже даже и не помню, когда в последний раз им пользовался.
0
  • avatar
  • Vga
  • 27 сентября 2012, 08:24
Как будто это много. Хотя да, на бейсике тех времен контролировать программу такого размера уже сложно.
Для середины 80х- много. Мог бы и больше, да необходимо было оставлять место в памяти под буфера для обрабатываемого кода, его создаваемого листинга (довольно обьемного), таблиц переходов, меток, и прочего.

Сейчас — да, у меня в программах на Дельфи, которые писал на работе, суммарный размер исходника 20-30 тысяч строк, до 10 и более тысяч строк в одном модуле — обычное дело…
0
  • avatar
  • SWG
  • 27 сентября 2012, 08:51
Ну, скорее потому, что больше и не лезло. Хотя контролировать программы более килостроки в парадигме этого бейсика уже непросто, согласен.
до 10 и более тысяч строк в одном модуле — обычное дело…
Это уже перебор (если не необходимость, конечно). Для того модули и придуманы, чтобы разбивать код на не слишком большие файлы.
0
  • avatar
  • Vga
  • 27 сентября 2012, 08:56
IF ELSE не намного лучше GOTO. Дзен это программирование вобще без условных переходов. Я в этом стиле уже некоторое время работаю, появляются свои приемы кодирования. Первоначально при создании макетки по железу вешаю сопли из проводов, по коду сопли из условных переходов. Когда вижу что девайс в принципе работает. Переделываю на нормальный код и железо.

Вот блок кода для светодиодной линейки POV. Машем рукой вправо буква рисуется справа. Машем рукой влево буква рисуется слева. Это пример рабочих соплей. Потом выложу нормальный код.

Timer2_Overflow:

    TST  Vibor;
    BRNE Obratny_Hod;

Pryamoy_Hod:

    LDI  ZH,HIGH(scText * 2);               ;????????? ????? ???????? ??????? ????
    LDI  ZL,LOW(scText * 2);
    RJMP Chtenie;

Obratny_Hod:

    LDI  ZH,HIGH(scText_Obr * 2);               ;????????? ????? ???????? ????????? ????
    LDI  ZL,LOW(scText_Obr * 2);

Chtenie:

    ADD  ZL,Counter;                        ;?????????? ?? ??????? ????????
    ADC  ZH,temp0;
    LPM  temp,Z+;                            ;????????? ???????

    sbi PortB,2;
    sbi PortB,1;
    OUT  SPDR,temp;
Whait:
    sbis SPSR,SPIF;
    rjmp Whait;

    LPM  temp,Z;
    OUT  SPDR,temp;
Whait2:
    sbis SPSR,SPIF;
    rjmp Whait2;

    cbi PortB,2;
    cbi PortB,1;

    INC  Counter;                           ;??????????? ???????
    INC  Counter;
    CPI  Counter,120;
    BRNE Ref_Out;

    CLR  Counter;
    LDI  temp,1;
    EOR  Vibor,temp;

Ref_Out:

    RETI;
0
Сомнительной полезности дзен. Нет, бывают случаи где он однозначно полезен, да и деревья из вложенных условий читаются с трудом, но этот «дзен» зачастую еще менее читабелен, и еще чаще — неоптимален.
0
  • avatar
  • Vga
  • 27 сентября 2012, 07:40
он — ДЗЕН.
он не может быть полезен или бесполезен.
так как он — ДЗЕН.
0
В программировании под дзеном обычно понимается несколько другое, более поддающееся прагматической оценке.
А относительно оригинального дзена я дискутировать не буду — это вне моей компетенции.
0
  • avatar
  • Vga
  • 27 сентября 2012, 08:30
лучше не будем разворачивать эту тему.
0
IF ELSE не намного лучше GOTO. Дзен это программирование вобще без условных переходов.
Утверждение весьма сомнительное. IF — THEN — ELSE позволяют не перебирать проверку всех условий, пропуская заведомо ненужные варианты, в отличие от голых IF.

Правда, я обычно использую ELSE (как и CASE) довольно редко, но где использую — там выигрыш от них есть. Ну и откомпилированные листинги постоянно просматриваю. Например, при небольшом количестве условий несколько IF компилируются короче, чем заменяющий их CASE. Но исполняться могут дольше. Всяко бывает.

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

Да много еще в программировании всяких «мелочей», помогающих писать компактный и оптимальный код, какой бы язык не использовался.
0
  • avatar
  • SWG
  • 27 сентября 2012, 08:31
В конструкции же IF — THEN — ELSE я всегда пускаю по прямой ветке наиболее вероятное из условий, которое выполняется чаще.
А вот я иногда по then пускаю более короткую (точнее — однострочную) ветку. Это позволяет охватить одним взглядом обе ветки if, если вторая очень длинная. Разница в оптимальности только при составном условии.
Также при проверке нескольких условий в одном IF обычно в Паскале при невыполнении одного из них проверка следующих за ним компилятором уже не делается
Это сокращенное вычисление условий. Довольно полезная фишка, позволяет писать условия вида if (Foo<>nil) and (Foo.Bar=10). Но всегда следует убедиться, что целевой язык/компилятор (а в паскале обычно у каждого компилятора свой диалект) поддерживает его.
Да много еще в программировании всяких «мелочей», помогающих писать компактный и оптимальный код, какой бы язык не использовался.
О да. И не меньше мелочей, требуемых чтобы писать правильный код, не стукаясь о подводные камни.
0
  • avatar
  • Vga
  • 27 сентября 2012, 08:40
Также при проверке нескольких условий в одном IF обычно в Паскале при невыполнении одного из них проверка следующих за ним компилятором уже не делается, с учетом этого располагаю условия в цепочке так, чтобы основные из них — располагались первыми, далее уже менее важные, уточняющие.

Ну этим отличается не только паскаль, но и С/С++/Java.
0
Проще поискать, какой из современных языков этого не умеет. Но вот со случаями, когда на это можно полагаться — сложнее. Скажем, тот же С может вычислить часть выражений в составном условии и успокоиться сразу же, как будет ясен результат, но далеко не факт, что порядок их вычисления будет задан.
А вот в Delphi он задан и там спокойно можно писать if Assigned(SomeObj) and (SomeObj.Foo>0) — предварительно убедившись, правда, что в настройках компилятора не отключено сокращенное вычисление.
0
  • avatar
  • Vga
  • 27 сентября 2012, 08:51
В сях и жабе он тоже задан. Более того, если бы он не был задан, пользоваться им было бы нельзя, кажем если мы проверяем указательн на ненуль и тут же проверяли что-то лежащие по этому указателю, то имели бы неопределенное поведение. Но в силу фиксированности порядка вычислений все работает аж бегомю.
0
Вообще, по идее, это всего лишь оптимизация для ускорения расчета булевых выражений. И в языке C для очень многого не указан порядок, там даже контрольных точек (или как они там называются, это точки, где все побочные эффекты предшествовавшего кода уже вступили в действие) весьма мало и куда меньше, чем думает большинство программистов. Поэтому я и усомнился в специфицированности порядка вычисления таких выражений.
Впрочем, конкретно стандарт я не читал, так что точно утверждать не могу. Хотя булево выражение, по идее — обычное выражение, а выражения в С могут вычисляться в любом порядке (ну, в тех пределах, где он не задан приоритетами).
Так что хотелось бы все же пруфлинк.
0
  • avatar
  • Vga
  • 27 сентября 2012, 10:32
0
Ну, вики не очень надежный источник. Но там есть ссылка на стандарт (хотя и довольно новый), где уточнено — да, для операторов && и || левый операнд вычисляется первым, после этого идет sequence point (это его я и назвал контрольной точкой) и только затем и только по необходимости вычисляется второй операнд. У оператора ?: тоже вычисляется только та ветка, которая возвращается в качестве результата.
Остается только вопрос — это так во всех стандартах языков С и С++?
И по самой статье — что за колонка eager operator в таблице? Непосредственный перевод слова ничего не проясняет.
0
  • avatar
  • Vga
  • 27 сентября 2012, 11:17
Остается только вопрос — это так во всех стандартах языков С и С++?
Да, так было отродясь. Насколько я понимаю, это следствие реализации разворачивания такого выражения «в лоб» в коде.
И по самой статье — что за колонка eager operator в таблице?
Не знаю как применительно к другим языкам, а в C/C++/Java это побитовые операции (в отличие от логических, которые в следующей колонке).
0
Да, так было отродясь. Насколько я понимаю, это следствие реализации разворачивания такого выражения «в лоб» в коде.
Нет. Во первых, полное вычисление в некоторых случаях даже быстрее, во вторых — тогда бы не был жестко задан порядок.
Не знаю как применительно к другим языкам, а в C/C++/Java это побитовые операции (в отличие от логических, которые в следующей колонке).
Для С там указано «none». По логике это скорее операции, которые проводят полное вычисление.
0
  • avatar
  • Vga
  • 27 сентября 2012, 11:56
Нет. Во первых, полное вычисление в некоторых случаях даже быстрее, во вторых — тогда бы не был жестко задан порядок.
Речь идет о самых первых реализациях. Там и оптимизатор-то не всегда был.

P.S. сейчас мало кто обращает внимание, но весь синтаксис С заточен под оптимальную компиляцию «в лоб» на PDP-11. там, собственно, и оптимизатор был не сильно нужен, поскольку в каком-то смысле С выглядел просто как альтернативный синтасис для тамошнего асма. да, и все богатство адресной арифметики в С тоже оттуда…
0
Неплохо бы все же подкрепить выдержками из стандартов ранних С. Они довольно сильно от нынешних отличаются.
0
  • avatar
  • Vga
  • 27 сентября 2012, 13:01
Что именно подкрепить? тем более, что стандартов до 89-го года вообще не было, а к тому времени С имел уже изрядный возраст. Но, кстати, даже в режиме K&R (то есть до ANSI C 89) тогдашние компиляторы вели себя точно так же и проверки делались по очереди в порядке написания. Что же до моих высказываний о синтаксисе, то это мои соображения основанные на знании особенностей системы команд PDP-11. Писать под нее не доводилось (хотя была возможность), но одно время у меня было что-то вроде хобби колупаться в архитектура разных компов, в том числе в системах команд и режимах адресации. Все эти адресации с пост- и пре- инкрементами и декрементами, возможность свободно использовать указатели в левой и правой частях присвоения это именно оттуда. Как и ++, --, +=, -=, /= и прочие причандалы.
0
Ну хотя бы выдержкой из стандарта C89. Кроме того, для ранних С функцию стандарта выполнял соответсвующий выпуск K&R, так что и там можно посмотреть.
Как и ++, --, +=, -=, /=
Да эти операции на любой двухадресной архитектуре такие. Да и на трехадресной прекрасно эмулируются.
0
  • avatar
  • Vga
  • 27 сентября 2012, 13:39
Ну хотя бы выдержкой из стандарта C89. Кроме того, для ранних С функцию стандарта выполнял соответсвующий выпуск K&R, так что и там можно посмотреть.
Сорри, у меня нет возможности уделить время поиску цитат.
Да эти операции на любой двухадресной архитектуре такие. Да и на трехадресной прекрасно эмулируются.
Ты не понял. Речь не о том, делаются они или нет. Просто на PDP-11 они чуть ли не в одну команду транслируются. Так же как и выражения типа *p++ = *s++;
0
Это ты не понял. Я о том, что конкретно эти транслируются одной командой на всех двухадресных архитектурах (т.е. большинстве), да и на трехадресных тоже (ADD R2, R2, R3 — аналог ADD R2, R3).
Вот выражения вроде *p++ = * s++ — более специфичные примеры.
0
  • avatar
  • Vga
  • 27 сентября 2012, 14:53
Ты упускаешь один важный момент: прежде чем сказать ADD R2, R3 надо операнды в регистры загрузить (а потом еще и выгрузить при необходимости). А вот на PDP-11 оно транслируется в одну инструкцию даже если операнды в памяти.
0
Ну, для load-and-store архитектур это так. Хотя с весьма приличной вероятностью операнды уже в регистрах (а на процах вроде attiny10 — они в принципе нигде кроме регистров быть не могут, кроме разве что данных из SFR). А вот x86 — вполне может взять и прямо из памяти.
0
  • avatar
  • Vga
  • 27 сентября 2012, 15:04
А вот x86 — вполне может взять и прямо из памяти.
Если мне память не изменяет, то на PDP-11 даже нечто вроде вот такого выльется в одну инструкцию: a += *b++;
Я это все не к тому, что подобное нельзя где-то реализовать или сложно компилировать, а к тому, что очень прозрачно прослеживается связь конструкций языка и возможности конкретной архитектуры на которой он создавался.
0
А что, на x86 это нельзя? Там же практически для любой команды — стопицот способов адресации. Оно конечно надо сверяться по справочнику, но думаю сложение с выборкой из памяти по указателю с постинкрементом есть.
Хотя за одну команду оно не исполнится. Процессор разложит ее на несколько микрокоманд в load-and-store стиле.
0
  • avatar
  • Vga
  • 27 сентября 2012, 18:05
А что, на x86 это нельзя?
Нет.
Там же практически для любой команды — стопицот способов адресации.
Насчет более новых процов не уверен, но в старых сложения память-память точно не было. Да и адресацией все далеко не так радужно.
0
Timer2_Overflow:

    SBI PortB,2;                            ;готовим драйвер LED
    SBI PortB,1;

    LDI  ZH,HIGH(scText * 2);               ;загружаем адрес буквы
    LDI  ZL,LOW(scText * 2);
    ADD  ZL,Counter;                        ;сдвигаемся на счетчик столбцов
    ADC  ZH,temp0;

    LPM  temp,Z;                            ;загружаем верхний байт столбца
    OUT  SPDR,temp;                         ;выдаем в SPI
Whait:
    SBIS SPSR,SPIF;
    RJMP Whait;

    LDS  temp,pIncrement;                   ;сдвигаемся
    ADD  Counter,temp;
    ADD  ZL,Counter;
    ADC  ZH,temp0;

    LPM  temp,Z;                            ;загружаем нижний байт столбца
    OUT  SPDR,temp;                         ;выдаем в SPI
Whait2:
    SBIS SPSR,SPIF;
    RJMP Whait2;

    CBI  PortB,2;                            ;зажигаем LED
    CBI  PortB,1;

    CPI  Counter,40;                         ;определяем направление сдвига
    BRNE PC+3;
    LDI  temp,-1;
    STS  pIncrement,temp;

    CPI  Counter,0;
    BRNE PC+3;
    LDI  temp,1;
    STS  pIncrement,temp;

    LDS  temp,pIncrement;                   ;сдвигаемся
    ADD  Counter,temp;

    RETI;


scText:          .DB   0x00, 0x00, 0x00, 0x00, 0x30, 0x7c, 0x70, 0xfe, 0xf1, 0xff, 0xf1, 0xff, 0xe1, 0xff, 0xc1, 0x83, 0xc1, 0x83, 0xc1, 0x83, 0xc1, 0x83, 0xc1, 0x87, 0xc1, 0x8f, 0xff, 0x8f, 0xff, 0x8e, 0xff, 0x8c, 0x7f, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00;


А это нормальный код где условные переходы только очень локальные или в циклах. Смысл программы, она нарезает изображение буквы по столбцам по пикселям и выдает их на светодиодную линейку. Высота буквы 2 байта = 16 пикселей светодиодов, ширина буквы 20 столбцов. Итого 2*20 = 40 байтов на выдачу чтобы прорисовать букву. Сначала выдаем слева направо. При движении назад справа налево. Это реальный код, в котором видна красота и сила ассемблера.
0
А, я тебя не так понял. Да, минимизация лишних условий — пользительно. Становится куда проще для понимания.
0
  • avatar
  • Vga
  • 28 сентября 2012, 16:23
пользительно
я еле вспомнил, у кого это словечко подцепил…
(в оффлайне. задолго до появления тут...)
0
А я и не вспомню. Тем более не такое оно редкое.
0
  • avatar
  • Vga
  • 28 сентября 2012, 22:24
ну, не редкое.
но у меня есть такая развлекуха — следить за чистотой языка (за что меня недавно слили, кстати, за наше обсуждение… ;) )
потому я у близкого круга (физически) или караю, или высмеиваю. что суть монопенисуально.
ну и как побочный эффект — слежу за своим. в том числе отслеживая, у кого что подцепил с размышлениями и поисками по словарям.

но это такой офф, что ща опять по шапке получим.
0
случайно не от Салтыкова-Щедрина?
0
не. не от него. от очень близкого знакомого. но надо перечитать классиков. спс за пинок.

(тоже такой режим суток? ;) )
0
(тоже такой режим суток? ;) )
Эээ… 24часа онлайн? :)
0
ночной. личное утро — общепринятым днем.
а онлайн — просто комп не тушить…
0
Я, вобщем, о себе :) У меня, как раз, почти так и получается, разве что есть короткий перерыв на сон :)
0
Ты забыл уточнить, что этот ваш день общепринят хорошо если в плюс-минус двух поясах. Нинай насчет Украины, а вот Россия поясов 8-10 накрывает. Так что кому-то это вполне утро/вечер.
0
  • avatar
  • Vga
  • 30 сентября 2012, 00:36
С учетом того, что я, обычно, сплю часа 3-4, то накрываю я ~5/6 всех часовых поясов :)
0
Я про соответсвие личного «утра» общепринятому «дню»)
0
  • avatar
  • Vga
  • 30 сентября 2012, 03:47
Дык это, у меня утро обычное, как у всех вокруг :)
0
Так я не тебе и отвечал.
0
  • avatar
  • Vga
  • 30 сентября 2012, 20:39
«Слушайте, дайте поспать!» Люди на работе, между прочим, а они тут помнюутро тут не утро.
0
На мой взгляд, спор о том использовать goto или другие операторы, также бессмыслен, как спор о том, что лучше: грузовая газель или спортивный бугатти. Надо скорость — оптимизируй люыми способами. Надо налопатить кучу кода, да чтоб всё было разложено по полочкам и очевидно даже школьнику — пожалуйста — используйте привычные для большинства конструкции. Автору однозначно плюс.
0
В определенных случаях goto резко повышает читабельность. И совсем не факт, что в остальных он повысит производительность.
0
  • avatar
  • Vga
  • 27 сентября 2012, 08:25
Этот код гораздо аккуратней будет выглядеть так:
Акуратность тут сомнительная, конечно, просто попытка рукопашной оптимизации. Если уж вам для таких целей понадобился goto, то есть вполне каноничный вариант в виде

    do {
        …
        if (a)
                break;
        …
        if (b)
                break;

        …
        с = 10;
        return 5;
    } while (0);
    
    c = 15;
    return 10;

Да и вообще, приведенные вами примеры меня как-то не убеждают их скорее надо приводить в качестве примера того, как писать не надо. Такой код хоть с goto, хоть без остается нечитабельным.
0
  • avatar
  • evsi
  • 27 сентября 2012, 08:45
Посмотрите на программу ниже:

char a, b, c;

for (a = 0; a < 10; ++a)
{
        for (b = 0; b < a; ++b)
        {
                if (!c)
                        goto Leave;
        }
        for (b = 10; b < 15; ++b)
        {
                d ();
        }
}

Leave:
e ();


Что происходит – понятно? Есть вложенный цикл. Если наступило какое-то условие – покидаем все последующие обработки.
В этом случае напрашивается легальный заменитель goto — break
Либо иногда continue
0
Основная проблема goto — с его помощью можно навертеть лапши.
То есть создать пересекающиеся циклы и другие запрещенные конструкции.
Другое дело, если это происходит путем автоматической кодогенерации — например, из схем дракона или через макросы protothreads.
В последнем случае они даже скрыты от прямого взгляда и подставляются на этапе компиляции препроцессором.
0
Прототредс не через goto работает, а через switch.
И запрещенные конструкции компилятор не даст создать (зависит от языка, впрочем).
А вот навертеть лапши — легко.
0
  • avatar
  • Vga
  • 27 сентября 2012, 11:19
запрещенные конструкции компилятор не даст создать
А ну-ка:

for(i=0;i<100;i++)
{
    Label:
    DoSomething();
}
...
goto label;
0
Label != label
0
Не придирайтесь :)
Я уже попробовал.
Все замечательно компилятор переварил (cigwin GCC), результат — зависон.
Я, правда, ещё усугубил — for(int i=0; i<100; i++)
0
То есть, мораль-то в чём — «легальные» заменители goto не могут нарушить структуру программы, а goto — запросто.
0
Что, даже без варнингов? И еще стоит поиграться с заданием степени придирчивости компилятора и стандарта языка. Вроде современные придираются к прыжкам внутрь циклов и блоков, и даже мимо инициализации переменных.
Алсо, рано или поздно оно отвиснет — когда i таки дотикает до 100 (это максимум maxint+100 итераций).
0
  • avatar
  • Vga
  • 27 сентября 2012, 13:04
Не, не отвиснет. Как только цикл закончится, goto закинет обратно в него же, и хоть пройдет всего одна итерация, после этого опять же закинет в него — и так далее пока i не дойдет до отрицательных значений, после чего цикл запустится на maxint/2 + 100 итерации, и все по кругу.
+1
А, ну это уже бесконечный цикл. Его и без goto организовать можно. Оно повиснет даже в том случае, если прыжок внутрь цикла — корректная и безопасная операция.
Для демонстрации некорректности прыжка в цикл лучше сделать так:
goto inloop;
for(int i = 0; i < 100; i++)
{
  inloop:
  DoSomething();
}

Т.е. здесь корректно все, кроме факта прыжка внутрь цикла. В DoSomething стоит посчитать число вызовов и потом вывести его.
Кроме того, для циклов без инициализации (while, do-while) такой прыжок вполне безопасен.
Прыжок мимо инициализации переменных для С тоже вполне безопасен — нет автоконструкторов.
цикл запустится на maxint/2 + 100 итерации
На maxint+100. MaxInt — 2^31-1 на 32-разрядных системах.
0
  • avatar
  • Vga
  • 27 сентября 2012, 15:00
Но локальным переменным нужно, бывает, выделить место на стеке, если регистры заняты чем-то другим. Если компилятор добавляет инициализацию внутренних цикловых переменных (сдвигает указатель стека и присваивает значения регистрам) перед goto и прочий нужный шлак, то ладно.
Если нет, то я даже не знаю, к чему это приведёт. Впрочем, переменные можно объявлять и вне блока, где совершается это варварство.

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

Хотя я не вижу какого-либо практического смысла в такой конструкции, как прыжок в тело цикла.
0
Вообще, обычно переменные выделяются единым блоком в начале функции — это дешевле по времени. Да и если бы int i выделялся в начале цикла и удалялся в конце — оно бы сорвало стек и упало (хотя, мож оно и сорвало, но из-за зацикливания на goto не дошло до перехода по мусорному адресу). Но вообще, такие вещи должны предупреждаться компилятором. Если внутрь цикла можно прыгать — значит хотя бы срыва стека и подобных последствий это не вызовет.
0
  • avatar
  • Vga
  • 27 сентября 2012, 18:08
Я согласен, что компилятор должен предупреждать о таком :)

Но пример с i не показателен — если переменных мало, они размещаются в регистрах, не затрагивая стек :) Стек начинается, когда регистров не хватает.

Ради любопытства, дома буду, посмотрю, что кейл генерит.
0
Так в том и дело, что был приведен пример (явно неудачный) с прыжком назад, а не вперёд, который не отвиснет.

К вопросу о количестве итераций — пока i не дойдет до 7FFFFFFF, проверка будет выкидывать из цикла после одной итерации. Дальше оно пройдет по отрицательным значениям от 80000000 до 0 таки за maxint/2 итераций, потом еще за 100 дойдет до 100 и опять вывалится из цикла.
0
Это не показательно. Так можно привести код с goto:
for(;;){
  inloop:
  DoSomething();
}
goto inloop;

И сказать «смотрите, оно с goto и виснет!». К тому же, быть может, если убрать бесконечный цикл — оно грохнется. Вот это — риальне ошибка)
0
  • avatar
  • Vga
  • 27 сентября 2012, 18:10
Ну что я могу добавить к уже высказавшимся…
Обоснование вбивания тезиса «goto — зло» дал Vga.
Про структурные break и continue тоже упомянули.

Завороченность алгоритма, скорее всего, означает, что декомпозиция задачи криво проведена. Не всегда, конечно, бывает, что по-другому никак — но нетипичные случаи не годятся как дидактический материал.

Да, goto таки бывает полезно, в частности для единообразной постобработки перед выходом из функции (что уже не так нетипично). Для таких случаев и использую.

Для меня множество флагов — куда большее зло. Попробуй разберись, кто из них где изменяется, и где анализируется. Как писал основоположник «SWITCH-технологии» Шалыто «Программы с флагами неустойчивы, как слоны с тонкими ножками».
0
  • avatar
  • juray
  • 27 сентября 2012, 14:00
Кто хотел примеров?

static int
clip_line(point_t *s, point_t *e)
{
	int		s_cc = clip_code(s);
	int		e_cc = clip_code(e);
	float		lim_w = (float) (screen_w - 2);
	float		lim_h = (float) (screen_h - 2);
	float		dx, dy;

	do {
		if (s_cc & e_cc)
			return -1;

		if (!(s_cc | e_cc))
			return 0;

		if (!s_cc) {

			int tmp = s_cc;
			s_cc = e_cc;
			e_cc = tmp;

			point_t *ptmp = s;
			s = e;
			e = ptmp;
		}

		dx = e->x - s->x;
		dy = e->y - s->y;

		if ((fabsf(dx) < 1.0f) && (s_cc & 0x03))
			return -1;

		if ((fabsf(dy) < 1.0f) && (s_cc & 0x0c))
			return -1;

		if (s_cc & 0x01) {
			s->y = e->y + ((0.0f - e->x) * dy) / dx;
			s->x = 0.0f;
			goto __cl_recalc;
		}

		if (s_cc & 0x02) {
			s->y = s->y + ((lim_w - s->x) * dy) / dx;
			s->x = lim_w;
			goto __cl_recalc;
		}

		if (s_cc & 0x04) {
			s->x = e->x + ((0.0f - e->y) * dx) / dy;
			s->y = 0.0f;
			goto __cl_recalc;
		}

		if (s_cc & 0x08) {
			s->x = s->x + ((lim_h - s->y) * dx) / dy;
			s->y = lim_h;
			goto __cl_recalc;
		}

__cl_recalc:
		s_cc = clip_code(s);
	}
	while (1);
}


А вот здесь goto в обратную сторону.

code.google.com/p/edutils/source/browse/src/lang/parser.c
0
У вас выполняется только 1-й сработавший if, что не очевидно. Желательно было применить конструкцию if — else if — else. goto здесь совершенно лишний.
0
Сейчас подумал, похоже можно было do {… break; } while(0)
0
А вот лексические/синтаксические анализаторы — да, сложны и в реализации и в понимании без применения goto — хороший пример.
0
Это сильно зависит от типа анализируемых грамматик. Рекурсивный спуск, к примеру, ничего такого не требует. А те, которые требуют, их никто не читает, поскольку код генерится. Да и не сильно его почитаешь, учитывая, что он работает по таблице. Впрочем, опять-таки, это зависит от типа грамматик и от типа анализитора.
0
Там парсятся вот такие строки.


        link + dd3.s dd1.o1 ;
        link + dd3.r dd2.o1 ;
        link dd3.nq nq ;
        link dd3.q q ;



        w = field | id.field
        link [+] [w] [w] ... ;


Как будет называться грамматика?

p.s. опять вижу, что все не так надо было делать.
0
Давно не брал я в руки шашек… Насколько я могу судить, грамматика легко вписывается в LL(1), а значит парсится рекурсивным спуском просто на ура.
0
Придумал новый оператор thzen.
0
Внесу свои пять копеек.

За статью автору спасибо, но ИМХО аргументы у него получились не очень убедительны. Первый пример – очень абстрактный, и автор ввел специальные ограничения, которые подталкивают нас к использованию goto. Другие примеры более конкретны, но тоже имеют изъяны. Например, в случае выхода из двойного цикла (варианты без goto) автор почему-то отказался от использования break и добавил проверку флага в каждую итерацию цикла (вложенного и внешнего).

Касательно самого вопроса – если у Вас есть желание проникнутся проблемой (а также есть много свободного времени) рекомендую прочитать статью уважаемого тов. Дейкстра «Go To Statement Considered Harmful» и «ответ» на эту статью от не менее уважаемого тов. Д. Кнута «Structured Programming with go to Statements».

Лично мое мнение – да, goto можно использовать в определенных случаях. Но вот четко сформулировать общее правило, в каких именно случаях целесообразно использовать goto, а в каких нет – практически нереально. Это очень индивидуально для каждого конкретного алгоритма/реализации, зависти от требований к реализации и т. д. Выбор остается на совести конкретного программиста.
0
  • avatar
  • e_mc2
  • 27 сентября 2012, 17:08
Статья была ранее опубликована на хабре, а значит под конкурс не подходит.
0
Понял, спасибо
0
О нем еще кто-то помнит?
0
  • avatar
  • Vga
  • 27 сентября 2012, 18:14
Я плюнул и забыл давно.
0
Зря, полезный ресурс. Местами.
0
Я про конкурс…
0
  • avatar
  • Vga
  • 27 сентября 2012, 19:44
Как минимум он есть и никуда не девался.
0
Угу. И тянется уже более года, и хорошо если процентов 20 статей набралось. Хотя, в принципе, если все обладатели годных статей дружно выкатят их на конкурс — то он пожалуй выйдет на финишную прямую.
0
  • avatar
  • Vga
  • 27 сентября 2012, 19:58
Для этого надо проводить агиткомпанию. Как на коте к примеру. Правда там последний конкурс собрал кучу говна…
0
… и никому не нужен, даже основателю (что ты сам не однократно говорил)…
Так что ничего не зря. Своей цели я уже добился (да, простимулировал конкурс), не имея при этом ни каких лишних для меня ограничений.
0
Вспомнил своего преподавателя по PL/1, глядя на это бурление волн. Раз в языке есть оператор GoTo – значит его применение ДОПУСТИМО. Но оно требует самодисциплины – вот и всё.

А запреты на использование GoTo – это, насколько я понимаю, фобии (артефакты сознания) тех, кто хоть раз видел листинги на первых версиях Basic.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.