switch

Гриша
Offline
Зарегистрирован: 27.04.2014

эх, как не умел я коды писать... так и... за-то камень занят, хотя можно и так его занять

void setup(void) {
	Serial.begin(57600);
	int i = 0;
beg:

asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
	goto beg; 

fin:
	Serial.println("ОК");
}

void loop(void) {}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

baby_in_Arduino пишет:

когда я учился никакой магии с && не было 

О какой магии речь? Указателей что ли не было? И адреса брать нельзя было?

 а как глянуть адреса этих указателей (меток)??? (в Serial)

sadman41
Offline
Зарегистрирован: 19.10.2016

Так это... Намекните хотя бы как этот адрес адреса (&&) называется по-научному. Я пятую главу К&&R пробежал и явного применения такой конструкции не увидел.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ворота пишет:

Блин, чёта не выходит, наверное, я какую-то тонкость забыл. 

Не понимаю, чего ты там забыл, хотя, неудивительно … но перепутал ты всё. Мой пример был не про использование GOTO, а про НЕиспользование – программа была 100% структурной и кошерной как карась.

В общем, коллеги, коль уж скоро речь зашла о структурности, позвольте и мне внести свои пять копеек. Дело было в Шанхае на скучнейшей конференции по технологии программирования, проводимой местным университетом. Юные китайские структурасты читали доклад за докладом о пользе структурности. Всё это было с таким подростковым задором и уверенностью в своей правоте, что от зевоты скулы ломило. Они все были молодые, продвинутые, талантливые … самым дураком там был я. А вот Ворота – тот, наоборот, был самым умным – он сел в «протяни-руку-доступности» от фуршетного стола, на котором стоял коньяк и весь день дегустировал … в общем, в конце дня мне пришлось «нести» его в такси.

Так вот, озверев от скуки, я начал «готовиться к своему докладу», который был запланирован на следующий день. Времени у меня было – «вагон» и я написал исключительно структурную программу! Она соответствовала всем семи принципам структурного программирования и была просто идеальной структурной программой – мечтой стуктураста! Программа была, конечно, не та, что я приведу сейчас – эта только демонстрирует для вас основной приём, а в той, наоборот, основной приём был замаскирован как можно сильнее, чтобы не догадались как всё просто. Но, я специально написал пример именно иллюстрирующий приём, т.к. сейчас у меня цель объяснить, а не поотроллить.

Итак, программа - всего 20 строк (сразу предупреждаю! Не пытайтесь замерять её бытовым говнокодометром! Зашкалит и сломается! Только профессиональным прибором, внесённым в реестр средств измерений!):

uint16_t doAll(void) {
	static uint16_t disp;
	if (analogRead(0) < 2000) {
		disp = (uint16_t)&&neverRun - (uint16_t)doAll ;
	} else {
		neverRun:
		Serial.println("In God We trust! Anyone else pay cash!");
	}
	Serial.println("End of doAll!");
	return (uint16_t)doAll + disp;
}

void setup(void) {
	Serial.begin(57600);
	Serial.println("Let's go on!");
	((void (*)(void))doAll())();
	Serial.println("The end!");
}

void loop(void) {}

Давайте на неё посмотрим. Условие в строке №3 всегда истинно – ну не может же analogRead так обкурится, чтобы выдать что-то большее, чем 2000! А это значит, что строки № 6 и 7 не выполняются никогда!

Тем не менее, любой из Вас может запустить эту программу хоть на ардуине, хоть в протеусе и увидеть результат её выполнения:

Let's go on!
End of doAll!
In God We trust! Anyone else pay cash!
End of doAll!
The end!

Опаньки! Так фраза «In God We trust! Anyone else pay cash!» таки печатается! Значит, строка №7 выполняется! Как? Когда?

Всё просто. Первая пара скобок в строке

((void (*)(void))doAll())();

это первый вызов функции doAll. При первом вызове, она выполняет строки №№ 4 и 10 и возвращает не что иное, как адрес метки neverRun! Просто адрес куска программы в памяти!

А вот вторая пара скобок в строке

((void (*)(void))doAll())();

Вызывает уже не функцию doAll с начала, а сразу, напрямую, кусок этой функции, начиная с метки neverRun, т.е. с адреса, который выернул первый вызов! Вот тут-то строка №7 и исполняется, а куда ей нахрен деваться, когда её прямо вызвали?!

Вот как-то так.

Но моя программа в Шанхае была строк на 200 и этот приём (вызов по метке) был тщательно замаскирован! И я троллил аудиторию как мог – перечислил все семь принципов структурного программирования, показал, что моя программа соответствует им всем, призвал всех писать именно такие программы … в общем, развлекался. Поразвлекайтесь и вы :-)

Кстати, вызов куска функции не с начала, как в моём примере, чреват тем, что никакие локальные переменные не будут иметь правильных значений. Бороться с этим можно при помощи замыканий. Но это уже немного не моя тема, у нас тут есть специалист по лямбдам, если ему будет не в лом, он объяснит и приведёт примеры.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

sadman41 пишет:
Намекните хотя бы как этот адрес адреса (&&) называется по-научному.
Не вопрос, он называется - rvalue reference. Определен в п. 11.3.2(2) стандарта ISO/IEC 14882:2017. Ссылка на файл, содержащий стандарт, имеется в Песочнице.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:

 а как глянуть адреса этих указателей (меток)??? (в Serial)

Преобразуйте в unsigned и печатайте сколько хотите. Ну, или, если printf пользуете, то поставьте там модификатор формата "p".

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Мда. Недельный запой мне обеспечен. :(    

GCC extends the C language with a unary && operator that returns the address of a label. This address can be stored in a void* variable type and may be used later in a goto instruction.

счас до Борланда доберусь, гляну, умеет ли он так же.  Скорее всего, неть, это местечковое gcc расширение. 

Upd.  Добрался.  Глянул.  Умеет. 

Затоплю я камин, стану пить... 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

DetSimen пишет:
Глянул.  Умеет.
Что, и мой говнокод с переходом по метке в другой функции тоже заработал? Фига-се!

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ЕвгенийП пишет:

DetSimen пишет:
Глянул.  Умеет.
Что, и мой говнокод с переходом по метке в другой функции тоже заработал? Фига-се!

это я не пробовал.  только то, что на скрине, массив меток и goto на их

baby_in_Arduino
Offline
Зарегистрирован: 21.07.2019

ЕвгенийП пишет:

((void (*)(void))doAll())();

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

только вот еще не понятка

в начале функции же что то вроде

push ebp

mov ebp, esp

а в конце 

mov esp, ebp

pop ebp

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

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

baby_in_Arduino пишет:

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

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

(За время, када ты отучился, пришла мода на модель вызова __fastcall :)  )  

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

baby_in_Arduino пишет:

может и сработает

Что значит "может"? Она работает так, как есть, загрузите и посмотрите.

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

baby_in_Arduino пишет:
если компилятор не генерирует стандартный пролог эпилог
В данном случае генерирует. Чтобы не генерировал нужно функции прописать атрибут "naked". В моём примере (когда нет никаких локальных переменных) это в достаточной мере пофиг, но по уму, конечно, надо следить за стеком. Только это ж пример говнокода - кто ж его по уму-то пишет?

С локальными переменными тоже можно побороться, но там нужно некоторое трюкачество с лямбдами. Нехай Пух напишет пример - он спец!

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

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

вот явно всё вышесказанное было для начинающих, равносильно если бы  тут начал про Кундалини вещать )))

b707
Offline
Зарегистрирован: 26.05.2017

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

sadman41
Offline
Зарегистрирован: 19.10.2016

DetSimen пишет:

GCC extends the C language with a unary && operator that returns the address of a label. This address can be stored in a void* variable type and may be used later in a goto instruction.

Case range тоже из области "GCC extends", так что, видимо, все эти приёмчики не сильно переносимы?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:

вот явно всё вышесказанное было для начинающих

А что, тут нельзя поговорить ни о чём, что не связано с подтиранием носов начинающим? Это типа епитимья такая - говорить можно не о том, что самому интересно, а только о том, что интересно начинающим?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

sadman41 пишет:

видимо, все эти приёмчики не сильно переносимы?

Здесь форум "ардуино", а в пределах платформы ардуино всё вполне переносится (с уны на мегу там, или на микру)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

sadman41 пишет:

Case range тоже из области "GCC extends", так что, видимо, все эти приёмчики не сильно переносимы?

Ну я ж перенёс это в Borland C++, причем в последнюю Rad Studio 10.2.3

sadman41
Offline
Зарегистрирован: 19.10.2016

DetSimen пишет:

sadman41 пишет:

Case range тоже из области "GCC extends", так что, видимо, все эти приёмчики не сильно переносимы?

Ну я ж перенёс это в Borland C++, причем в последнюю Rad Studio 10.2.3

А в ём какой компилятор?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

sadman41 пишет:

DetSimen пишет:

sadman41 пишет:

Case range тоже из области "GCC extends", так что, видимо, все эти приёмчики не сильно переносимы?

Ну я ж перенёс это в Borland C++, причем в последнюю Rad Studio 10.2.3

А в ём какой компилятор?

там свой какой-то, не GNU точно

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

ua6em пишет:

вот явно всё вышесказанное было для начинающих

А что, тут нельзя поговорить ни о чём, что не связано с подтиранием носов начинающим? Это типа епитимья такая - говорить можно не о том, что самому интересно, а только о том, что интересно начинающим?

шутю я, шутю...
Надысь опять ролик известного автора смотрел, про указатели, разыменование и прочее и тут в форуме эта тема засветилась...
А вот не могу понять, печатаю так и так а результат одинаковый:

Serial.println(uint16_t(labs[0]),HEX);
Serial.println(uint16_t(*labs[0]),HEX);

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, кто ж Вас знает, что Вы там печатаете, я же не вижу как у Вас labs описан и что в нём сидит.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Ну, кто ж Вас знает, что Вы там печатаете, я же не вижу как у Вас labs описан и что в нём сидит.

Это не у меня, это ваш пример  )))

Хотел, чтобы ардуина осталась для меня просто чёрным ящиком, с вами останется... ЩАС...

 In LOOP
3D6
3D7
3D8
3D9

)))
Адреса ссылок на метки выходят, что последовательны, занимают по байту и, там кроме джампа команды не вместится
 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

В моём примере ничего не печатается :-(

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

В моём примере ничего не печатается :-(

Это да, непорядок, всё приходится делать самому, всё самому, пытаюсь получить физические адреса ссылок (в мониторе порта)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

b707 пишет:

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

Переходы по метке в СП не запрещены, если они неявные (без goto) и скрыты фиговым листком. Например, case - чистой воды метка, или там continue с break, я уж не говорю про исключения.

А по поводу, что соответствует, а что - нет, Дейкстра сформулировал семь принципов СП (в википедии они прямо "по пальцам" перечислены, посмотрите). Смотрим на них и ... и что у меня нарушено? :-) Китайцы пытались оспорить нарушение части принципов, но так, как нарушаются они неявно, спор завершился вничью.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ЕвгенийП пишет:

А по поводу, что соответствует, а что - нет, Дейкстра сформулировал семь принципов СП (в википедии они прямо "по пальцам" перечислены, посмотрите). Смотрим на них и ... и что у меня нарушено? :-) Китайцы пытались оспорить нарушение части принципов, но так, как нарушаются они неявно, спор завершился вничью.

Привет! Тогда забыл вставить, а сейчас вспомнил: принцип "один вход - один выход" 5-й или 6-ой? - не помню. Вот он у тебя в малом примере (в этой теме) нарушен. Есть "черный ход". ;))) Китайского твоего примера не видел. Может там и лучше запрятал.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Да, там запрятал. Здесь-то я говно-технику показывал, а там скрывал :-)