Cтранное поведение swith
- Войдите на сайт для отправки комментариев
Пт, 03/07/2020 - 12:21
Здравствуйте! Столкнулся со странным поведением swith. Подскажите, пожалуйста, где ошибка. Вот этот блок кода работает неправильно.
switch (stste_menu) {// Показываем, нам не жалко! case 0: Serial.println ("Текущее время ХХ:ХХ"); //displayClock(uint8_t hrs, uint8_t mins); disp.point (1); break; case 1: Serial.print ("Обороты в минуту: "); Serial.println (rpm); if (rpm >= 1000) { DispData [0] = _H; DispData [1] = _H; DispData [2] = _H; DispData [3] = _P; } else { DataNew = rpm; Data = DataNew / 100; DispData [0] = int_to_byte (Data); DataNew -= Data * 100; Data = DataNew / 10; DispData [1] = int_to_byte (Data); DataNew -= Data * 10; Data = DataNew / 1; DispData [2] = int_to_byte (Data); DispData [3] = _P; } disp.displayByte(DispData); disp.point (0); break; case 2: Serial.print ("Скорость: "); Serial.println (km_h); if (km_h < 100) { DataNew = km_h * 10; Data = DataNew / 100; DispData [0] = int_to_byte (Data); DataNew -= Data * 100; Data = DataNew / 10; DispData [1] = int_to_byte (Data); DataNew -= Data * 10; Data = DataNew / 1; DispData [2] = int_to_byte (Data); DispData [3] = _U; } else { DispData [0] = _H; DispData [1] = _H; DispData [2] = _H; DispData [3] = _U; } disp.displayByte(DispData); disp.point (1); break; case 3: Serial.print ("Температура: "); Serial.println (temp); if (temp < 100) { DataNew = temp * 10; Data = DataNew / 100; DispData [0] = int_to_byte (Data); DataNew -= Data * 100; Data = DataNew / 10; DispData [1] = int_to_byte (Data); DataNew -= Data * 10; Data = DataNew / 1; DispData [2] = int_to_byte (Data); DispData [3] = 0x63; } else { DispData [0] = _H; DispData [1] = _H; DispData [2] = _H; DispData [3] = 0x63; } disp.displayByte(DispData); disp.point (1); break; case 4: Serial.print ("Дистанция за поездку: "); Serial.println (dist_metr); if (dist_metr < 1000) { DataNew = dist_metr; Data = DataNew / 100; DispData [0] = int_to_byte (Data); DataNew -= Data * 100; Data = DataNew / 10; DispData [1] = int_to_byte (Data); DataNew -= Data * 10; Data = DataNew / 1; DispData [2] = int_to_byte (Data); DispData [3] = 0x52; disp.displayByte(DispData); disp.point (0); } else if (dist_metr >= 1000 && dist_metr < 100000) { DataNew = dist_metr / 100; Data = DataNew / 100; DispData [0] = int_to_byte (Data); DataNew -= Data * 100; Data = DataNew / 10; DispData [1] = int_to_byte (Data); DataNew -= Data * 10; Data = DataNew / 1; DispData [2] = int_to_byte (Data); DispData [3] = 0x52; disp.displayByte(DispData); disp.point (1); } break; case 5: Serial.print ("Время поездки: "); Serial.println (time_trip_min); disp.displayClock(time_trip_min / 60, int(time_trip_min) % 60); if (millis () - timer_clock_trip >= 1000) { timer_clock_trip = millis (); state_point = !state_point; disp.point (state_point); } break; case 6: Serial.print ("Всего километров: "); Serial.println (mileage_km); if (mileage_km < 1000) { DataNew = mileage_km; Data = DataNew / 100; DispData [0] = int_to_byte (Data); DataNew -= Data * 100; Data = DataNew / 10; DispData [1] = int_to_byte (Data); DataNew -= Data * 10; Data = DataNew / 1; DispData [2] = int_to_byte (Data); DispData [3] = _S; disp.displayByte(DispData); disp.point (0); } else if (mileage_km >= 1000) { DataNew = mileage_km / 100; Data = DataNew / 100; DispData [0] = int_to_byte (Data); DataNew -= Data * 100; Data = DataNew / 10; DispData [1] = int_to_byte (Data); DataNew -= Data * 10; Data = DataNew / 1; DispData [2] = int_to_byte (Data); DispData [3] = _S; disp.displayByte(DispData); disp.point (1); } break; case 7: Serial.print ("Всего часов: "); Serial.println (mileage_time_hour); if (mileage_time_hour < 1000) { DataNew = mileage_time_hour; Data = DataNew / 100; DispData [0] = int_to_byte (Data); DataNew -= Data * 100; Data = DataNew / 10; DispData [1] = int_to_byte (Data); DataNew -= Data * 10; Data = DataNew / 1; DispData [2] = int_to_byte (Data); DispData [3] = _H; disp.displayByte(DispData); disp.point (0); } else if (mileage_time_hour >= 1000) { DataNew = mileage_time_hour / 100; Data = DataNew / 100; DispData [0] = int_to_byte (Data); DataNew -= Data * 100; Data = DataNew / 10; DispData [1] = int_to_byte (Data); DataNew -= Data * 10; Data = DataNew / 1; DispData [2] = int_to_byte (Data); DispData [3] = _H; disp.displayByte(DispData); disp.point (1); } break; case 8: Serial.print ("Проценты батарейки: "); Serial.println (percent_batt); int NewPercent = percent_batt; if (percent_batt < 100 && percent_batt >= 10) { NewPercent = (NewPercent * 100) + 8; } else if (percent_batt < 10) { NewPercent = (NewPercent * 1000) + 8; } else if (percent_batt == 100) { NewPercent = (NewPercent * 10) + 8; } disp.displayInt (NewPercent); disp.point (0); break; case 9: Serial.print ("Средняя скорость:"); if (time_trip_min == 0) { Serial.println (0); disp.displayByte (_empty, _empty, _0, _A); disp.point (0); } else { Serial.println (((dist_metr / time_trip_min) * 0.06)); if (((dist_metr / time_trip_min) * 0.06) < 100) { DataNew = ((dist_metr / time_trip_min) * 0.06) * 10; Data = DataNew / 100; DispData [0] = int_to_byte (Data); DataNew -= Data * 100; Data = DataNew / 10; DispData [1] = int_to_byte (Data); DataNew -= Data * 10; Data = DataNew / 1; DispData [2] = int_to_byte (Data); DispData [3] = _A; } else { DispData [0] = _H; DispData [1] = _H; DispData [2] = _H; DispData [3] = _A; } disp.displayByte (DispData); disp.point (0); } break; }
Когда переменная stste_menu становится равной 9, в порт ничего не выводится. Но. Когда я объявляю переменную NewPercent не после
case 8:
а глобально, все работает хорошо. В чем может быть проблема?
Извините, пожалуйста за неправильное название switch в названии, опечатался
и канпилятор тебе никаких warning-ов не пишет?
Когда переменная stste_menu становится равной 9, в порт ничего не выводится.
А она точно становится равной 9? В приведенном коде этого не видно
Комбинация условий строк 193 - 199 -полный бред, но влияет ли это на вывод case 9 - сказать сложно
короче, после case 8: поставь {
а закрывающую } - перед break этого блока.
Работает! А почему?
аптамуш, что внутри switch локальные (для одной ветки case) переменные могут быть только внутри блока {}.
я ж не зря тебя спрашивал, а не ругается ли канпилятор, например на этот код
примерно такими словами:
ну так у тебя, наерна, все ворнинги отключены же, как у страуса.
Чьордвозмьи, надо бы записать )))
Спасибо!
В принципе, можно и без скобок, но нельзя в одной строке и объявить, и инициализировать
Поскольку полной программы нет, то предполагаю, что stste_menu не становится 9-ти.
короче, после case 8: поставь {
а закрывающую } - перед break этого блока.
Не - в свитче он выполняет последовательно до сброса. Скобки не важны.
Скобки важны при объявлении переменных внутри switch
При инициализации.
Что то "ходячая энциклопедия" ЕвгенийП молчит...) Почему бы не объяснить людЯм? А то я смотрю никто так и не понял почему объявлять можно, а инициализировать переменную после кейса нельзя.
Без фигурных скобок имеется ввиду.
Почему бы не объяснить людЯм?
В отличие от Вас, нет у меня "излагательного" таланта, могу только на пальцах.)
Я столкнулся с этим пару лет назад, потому и запомнил.)
Сразу после switch получится впихнуть int a=1?
Зелёный, ты чучуть ошибаешься, при инициализации переменной в case ошибки нет, даже ворнинга нет, есть просто note от канпилятора, просто тогда почему-то блоки из нижних case никада не выполняются. Внимательно посмотри выдачу канпилятора в #8.
Странно тогда. Неуправляемый компилятор какой-то - сам декларации переносит куда удобно, а пользователю не даёт
Или я что-то не так понял? Што там после мясорубки ассемблеровской за фарш получается?
Зелёный, ты чучуть ошибаешься, при инициализации переменной в case ошибки нет, даже ворнинга нет, есть просто note от канпилятора, просто тогда почему-то блоки из нижних case никада не выполняются. Внимательно посмотри выдачу канпилятора в #8.
Проверил.
Может от версии IDE (компилятора) зависит?
В отличие от Вас, нет у меня "излагательного" таланта, могу только на пальцах.)
Дело в не в излагательном таланте. У Вас (судя по Вашим примерам и выводам из них) нет понимания логики вопроса, потому Вы выхватываете отдельные факты и не видите леса за деревьями. И кстати,
Давайте посмотрим:
Можем. Компилятор предупредит, что у нас что-то не так, но честно всё скомпилирует.
<
Но, тут прямая ошибка - он создаётся именно в этом месте. Это очень легко проверить. Возьмите не int, а что-нибудь, имеющее конструктор. Поставьте печать перед объявлением, в конструкторе и после объявления - увидите, что создаётся точно в том месте, где объявлено. Вот, пример:
Результат
Я столкнулся с этим пару лет назад, потому и запомнил.)
Зря тогда же не разобрались. Там всё просто и, если понимать суть дела, то ничего загадочного.
Я тоже не разбирался. :-) Не работает, значит на то есть у него своя сакральная правда. :-) Няхай.
А какая переменная 'а' будет использоваться в строке #17 (case 3, a=3), если декларация не будет выполнена?
Всё равно что такая:
Насчёт ошибок.
Всё равно что такая:
Однако у ЕвгенияП инит происходит в том же кейсе, где и объявление произведено. Не складывается у меня картинка
Декларация не может быть выполнена или не выполнена - она неисполняема. Не выполненной может быть инициализация. Объявление же оно как скрипач - всегда в законе. Например, вот вполне корректная запись:
Насчёт ошибок.
Либо там выбрана опция "трактовать предупреждения, как ошибки", либо это самодеятельность реализации. Язык такое вполне позволяет и, как видите, GCC (который с IDE) вполне это ест. Предупреждает о странности, но ест.
Просто я синтаксически ожидал, что компилятор, перепрыгнув на кейс 3, проигнорирует объявление переменной. А он декларации без инициализации выносит в начало блока {}, выходит.
Кстати, а чего Вы собственно к свичу привязались? Ситуация-то общая для любого "обхода инициализации", свич тут не при делах, он только частный случай. Вот, например, то же самое (и по сути, и по сообщениям) безо всякого свича.
Тема про свич, слово за слово, как говорится...
Понятно, что для одного его такую петрушку не стали бы разработчики сажать - механизьм универсальный.
Ничего он никуда не выносит.
Ну, можно говорить, что выносит, но тогда уж всё - хоть с инициализацией, хоть без.
В общем, существует очень расхожее мнение о том, что локальные переменные видимы "с момента объявления и до конца блока" (например). Это в принципе неверно. Это упрощение для чайников (для первоначального объяснения). Но когда нечайник пытается понять какую-то языковую тонкость, то упрощение ему мешает! Вот тот самый случай.
Область видимости переменной - весь блок. Но пользоваться ею можно только после объявления (здесь "после" надо буквально понимать как "ниже по тексту"). И инициализация её происходит в месте объявления!
Т.е. смотрите, программ входит в блок. Если у нас нет изощрённой оптимизации, то в момент входа в блок на стеке (или где там ещё) резервируется место под все переменные, объявленные внутри блока (в любом его месте). Но пользоваться переменой можно только начиная с точки объявления.
Почему так сложно - это пережиток тех времён, когда переменные в Си можно было объявить только в начале блока. С тех пор разрешили объявлять где попало, но делать выделение памяти на стеке где попало - очень сложно и часто неоднозначно. Потому сделано так, как сделано.
Свич же - самый обыкновенный блок (один) с метками и механизмом перехода на эти метки. Но это именно блок и метки (в стандарте они так и называются "Case labels" и это просто разновидность метки). Более того, они даже не обязаны находиться на одном блочном уровне. Метки же не обязаны! Вот смотрите, на пример, где разные кейсы находятся на разных уровнях иерархии блоков. И это не мешает им нормально работать:
а вот ещё большее извращенство, но тоже нормально работает
дайте HIGH на третий пин и он радостно скажет ONE
Область видимости переменной - весь блок. Но пользоваться ею можно только после объявления (здесь "после" надо буквально понимать как "ниже по тексту"). И инициализация её происходит в месте объявления!
Хочу уточнить про буквальность: т.е. в кейсе, что находится выше объявления, обращение к переменной будет ошибочно, а в кейсе пониже - уже валидным? Т.е. как понимать слово "можно" - какие последствия будут при обращении до объявления? К сожалению на телефоне канпилятора нет - проверить нет возможности, а сам я такими штуками не балуюсь - стараюсь, чтобы все было или квадратно или покрашено в зелёный цвет.
Если это так, то тут, полагаю, появляется интересная деоптимизирующая способность такого извращения. Я читал, что оптимизатор хитрым образом строит таблицу переходов и джамп на кейс 3 вполне в может быть ближе джампа на кейс 1. Так что, ежели требуется буквальная трактовка последовательности кейсов, то оптимизатор бреется. Или я уже ударился в фантазии?
del
Ещё раз. Фигурные скобки после switch - это самый обыкновенные блок. Выражения case - просто метки и не более. Никакого влияния на области видимости переменных они (кейсы) не оказывают от слова совсем. Просто блок, а внутри метки. Вот это нужно понимать чётко.
А теперь, понимая, что это самый обыкновенный блок, кейсы блоками не являются, а являются просто метками, сами ответьте на свой вопрос. Что будет если в любом блоке объявить переменную после использования? Или начать использовать до объявления? Будет сообщение про необъявленную переменную и ничего более.
Я ответил?
Я читал, что оптимизатор хитрым образом строит таблицу переходов и джамп на кейс 3 вполне в может быть ближе джампа на кейс 1. Так что, ежели требуется буквальная трактовка последовательности кейсов, то оптимизатор бреется.
Не очень понял о чём это, но прошу заметить, что если не будет брейков и сработает первый из кейсов, то все остальные кейсы будут выполняться строго в той последовательности, в которой они написаны - сверху-вниз и никакой оптимизатор этого не изменит.
Насчёт ошибок.
Либо там выбрана опция "трактовать предупреждения, как ошибки", либо это самодеятельность реализации. Язык такое вполне позволяет и, как видите, GCC (который с IDE) вполне это ест. Предупреждает о странности, но ест.
Что значит "это самодеятельность реализации"? Командная строка перед Вами, никаких опций.
Но, тут прямая ошибка - он создаётся именно в этом месте. Это очень легко проверить. Возьмите не int, а что-нибудь, имеющее конструктор. Поставьте печать перед объявлением, в конструкторе и после объявления - увидите, что создаётся точно в том месте, где объявлено.
С конструктором не убедили. Думаю, это не чистый эксперимент.)
del
Что значит "это самодеятельность реализации"?
Так называют ситуацию, когда авторы реализации отходят от стандарта языка (расширяют язык или сужают его). Типичный пример - диапазонные кейсы в GCC - в языке такого нет.
Командная строка перед Вами, никаких опций.
И что, я не знаю какие там опции по умолчанию. У меня в IDE Ваш пример нормально скомпилировался. С предупреждениями, но без ошибок. И с точки зрения языка это правильно.
С конструктором не убедили. Думаю, это не чистый эксперимент.)
Читайте стандарт языка, может тогда убедитесь :-)
sadman41,
на самом деле, там всё ещё смешнее. Как я уже сказал, главное понимать, что фигурные скобки после switch - это самый обыкновенные блок в начале которого есть пачка if'ов c goto. Выражения case - просто метки и не более. Если это понимать, то можно смело предполагать как именно он сработает в той или иной ситуации. ну, вот, например, посмотрите на эту шикарную конструкцию (задача сформулирована в комментариях) - и работает ведь!
Надеюсь, мисрасты, структурасты и прочие сектанты этого не увидят, иначе быть мне съеденным под соусом из дерьма. Но, как видите, оператор switch - это просто кладезь возможностей для истинно-творческого человека :-)))
P.S. если кто-то хочет сказать мне, что за переходы внутрь блоков if и else, минуя собственно проверку условия, мне предстоит гореть в аду - станьте в очередь, ибо Вы не первый. А что до ада, так там будет отличная компания - Кнут, Брукс и др., а в раю что ... с Виртом что-ли тёрки тереть?
Так называют ситуацию, когда авторы реализации отходят от стандарта языка (расширяют язык или сужают его). Типичный пример - диапазонные кейсы в GCC - в языке такого нет.
И что, я не знаю какие там опции по умолчанию. У меня в IDE Ваш пример нормально скомпилировался. С предупреждениями, но без ошибок. И с точки зрения языка это правильно.
Тут скорее НАШ IDE отходит от стандарта. Возьмём GCC4.9.2 для старшего брата:
Green,
я уже второй день Вам говорю, что в стандарте НЕТ запрета на обход инициализации при помощи перехода на метку операторами goto или switch. Да, это идиотизм, но это незапрещённый идиотизм. Вот, например, запрет входить внутрь блока catch при помощи goto/switch есть - да, это запрещено. А обходить инициализацию - нет такого запрета.
Поэтому, если Вы заявляете, что
Тут скорее НАШ IDE отходит от стандарта.
то уж будьте последовательны - и укажите какой именно параграф стандарта запрещает такие переходы и (стало быть) нарушается компилятором GCC, который поставляется с IDE.
Утверждаете, что такой запрет есть - покажите.
Нет, я не знаю стандартов и у меня нет желания с ними разбираться. Но я вижу что более старые версии IDE (другие версии GCC) дают ошибку ввиду невозможности инициализации локальной переменной в блоке switch без задания дополнительного блока, в отличие от свежих версий IDE (у меня 1.8.12), которые ограничиваются предупреждением. При этом Вы считаете что все эти другие версии неправильные, что весьма странно, не находите?
Касательно наших с Вами расхождений, как я понимаю, они в сравнении блока switch с обычным блоком. Вы утверждаете что разницы нет, тогда как разница очевидна. И это видно на примерах. И я тоже уже второй день именно об этом Вам говорю.
Давайте Вы попробуете чётко и без шелухи ответить именно на этот вопрос. В чём же всё-таки разница между switch и любым другим блоком? Ну, не считая синтаксического сахара типа того, что пачку проверок и goto компилятор сам вставит, а другом блоке пришлось бы руками писать.
В который раз?
Я в упор не вижу (не хочу видеть) никаких проверок и никаких goto.
Разница в том что мы не можем инициализировать локальную переменную.
В одной версии компилятора получаем ошибку, в другой предупреждение.
Это нужно рисовать или и так понятно?
Грин, для меня всё ясно стало в #34. А ты пошто споришь?