Cтранное поведение swith

Савелий
Offline
Зарегистрирован: 26.10.2019

Здравствуйте! Столкнулся со странным поведением 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:

а глобально, все работает хорошо. В чем может быть проблема?

Савелий
Offline
Зарегистрирован: 26.10.2019

Извините, пожалуйста за неправильное название switch в названии, опечатался

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

и канпилятор тебе никаких warning-ов не пишет?

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Савелий пишет:

Когда переменная stste_menu становится равной 9, в порт ничего не выводится.

А она точно становится равной 9? В приведенном коде этого не видно

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

Комбинация условий строк 193 - 199 -полный бред, но влияет ли это на вывод case 9 - сказать сложно

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

короче, после case 8: поставь {

а закрывающую } - перед break этого блока. 

Савелий
Offline
Зарегистрирован: 26.10.2019

Работает! А почему?

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

аптамуш, что внутри switch локальные (для одной ветки case) переменные могут быть только внутри блока {}.

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

я ж не зря тебя спрашивал, а не ругается ли канпилятор, например на этот код 

    switch (Msg.Message)
    {
    case msg_Error:
        int ErrorCode = Msg.LoParam;
        Serial << ErrorCode << eoln;
        break;
    default:
        break;
    } 

примерно такими словами: 

LCD5110Test.ino: In function void Dispatch(TMessage&)
LCD5110Test.ino: 58:5: warning: jump to case label [-fpermissive]
   default
   ^~~~~~~
LCD5110Test.ino:55: note    crosses initialization of int ErrorCode
   int ErrorCode = Msg.LoParam

ну так у тебя, наерна, все ворнинги отключены же, как у страуса. 

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Чьордвозмьи, надо бы записать )))

Савелий
Offline
Зарегистрирован: 26.10.2019

Спасибо!

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

В принципе, можно и без скобок, но нельзя в одной строке и объявить, и инициализировать

switch (x)
{
    case 1:
        int z; // ок, объявление разрешено
        z = 5; // ок, операция присваивания разрешена
        break;
 
    case 2:
        z = 6; // ок, переменная z была объявлена выше, поэтому мы можем использовать её здесь
        break;
 
    case 3:
        int c = 4; // нельзя, вы не можете инициализировать переменные внутри case
        break;
 
}
mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

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

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

DetSimen пишет:

короче, после case 8: поставь {

а закрывающую } - перед break этого блока. 

Не - в свитче он выполняет последовательно до сброса. Скобки не важны.

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

mykaida пишет:
Скобки не важны.
Скобки важны при объявлении переменных внутри switch

Green
Offline
Зарегистрирован: 01.10.2015

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

Скобки важны при объявлении переменных внутри switch

При инициализации.

Green
Offline
Зарегистрирован: 01.10.2015

Что то "ходячая энциклопедия" ЕвгенийП молчит...) Почему бы не объяснить людЯм? А то я смотрю никто так и не понял почему объявлять можно, а инициализировать переменную после кейса нельзя.
Без фигурных скобок имеется ввиду.

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

Green пишет:

Почему бы не объяснить людЯм?

Так, люди лучше знают!! Вы же поправили меня, что скобки важны только при инициализации, вот и объясняйте, а я послушаю. Я не могу такого объяснить, я эту картину по-другому себе представлял :-)))

Green
Offline
Зарегистрирован: 01.10.2015

В отличие от Вас, нет у меня "излагательного" таланта, могу только на пальцах.)
 

// Мы не можем инициализировать локальную переменную внутри кейса, т.к. инициализация 
// переменной требует выполнения, а case 1 может никогда не выполниться 
switch (x) {
  case 1:
    int a = 1;                          //инициализируем - ошибка, даже если переменная а
    break;                              // не будет использоваться в других кейсах

  case 2:
    break;
}

// Как говорили ранее, блок внутри кейса, позволяет инициализировать переменную.
// Но вне блока (в других кейсах) она, само собой, будет не видна
switch (x) {
  case 1: {
    int a = 1;                          //OK
    //...
    break;                              
  }

  case 2:
    a = 2;                              //ошибка
    break;
}

// Здесь всё красиво. Особенность в том, что хотя переменная определяется внутри кейса, но
// она не создаётся в этом месте, а помещается в начало блока, т.е, сразу после switch { 
switch (x) {
  case 1:
    break;

  case 2:
    int a;                              //определяем
    a = 2;
    break;
    
  case 3:
    a = 3;                              //работаем и в других кейсах
    break;
}

Я столкнулся с этим пару лет назад, потому и запомнил.)

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

Сразу после switch получится впихнуть int a=1?

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

Зелёный, ты чучуть ошибаешься, при инициализации переменной в case ошибки нет, даже ворнинга нет, есть просто note от канпилятора, просто тогда почему-то блоки из нижних case никада не выполняются.  Внимательно посмотри выдачу канпилятора в #8. 

Green
Offline
Зарегистрирован: 01.10.2015

sadman41 пишет:
Сразу после switch получится впихнуть int a=1?
Нет, ибо не выполнится.

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

Green пишет:

sadman41 пишет:
Сразу после switch получится впихнуть int a=1?
Нет, ибо не выполнится.


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

Или я что-то не так понял? Што там после мясорубки ассемблеровской за фарш получается?

Green
Offline
Зарегистрирован: 01.10.2015

DetSimen пишет:

Зелёный, ты чучуть ошибаешься, при инициализации переменной в case ошибки нет, даже ворнинга нет, есть просто note от канпилятора, просто тогда почему-то блоки из нижних case никада не выполняются.  Внимательно посмотри выдачу канпилятора в #8. 


Проверил. 

void setup() {
    int x = 0;
    switch (x) {
    case 1:
      int a = 1;                          //инициализируем - ошибка, даже если она
      break;                              // не будет использоваться в других кейсах
  
    case 2:
      break;
  }
}

void loop() {
}
In function 'void setup()':
8:10: warning: jump to case label [-fpermissive]
     case 2:
          ^
5:11: note:   crosses initialization of 'int a'
       int a = 1;                          //инициализируем - ошибка, даже если она
           ^
5:11: warning: unused variable 'a' [-Wunused-variable]
 
Скетч использует 444 байт (1%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.
 
Да, ты прав, ошибки нет. Но тогда у меня было: error: jump to case label [-fpermissive]
Может от версии IDE (компилятора) зависит?
 
Green
Offline
Зарегистрирован: 01.10.2015

sadman41 пишет:
Green пишет:

sadman41 пишет:
Сразу после switch получится впихнуть int a=1?
Нет, ибо не выполнится.

Странно тогда. Неуправляемый компилятор какой-то - сам декларации переносит куда удобно, а пользователю не даёт Или я что-то не так понял? Што там после мясорубки ассемблеровской за фарш получается?

Нормально.) Переносит объявления, а не инициализацию.
warning: statement will never be executed [-Wswitch-unreachable]
 

 

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

Green пишет:

В отличие от Вас, нет у меня "излагательного" таланта, могу только на пальцах.)

Дело в не в излагательном таланте. У Вас (судя по Вашим примерам и выводам из них) нет понимания логики вопроса, потому Вы выхватываете отдельные факты и не видите леса за деревьями. И кстати,

Давайте посмотрим:

Green пишет:

// Мы не можем инициализировать локальную переменную внутри кейса, т.к. инициализация 
// переменной требует выполнения, а case 1 может никогда не выполниться 
switch (x) {
  case 1:
    int a = 1;                          //инициализируем - ошибка, даже если переменная а

Можем. Компилятор предупредит, что у нас что-то не так, но честно всё скомпилирует.

Green пишет:

// Здесь всё красиво. Особенность в том, что хотя переменная определяется внутри кейса, но
// она не создаётся в этом месте, а помещается в начало блока, т.е, сразу после switch { 
switch (x) {
  case 1:
    break;

  case 2:
    int a;                              //определяем
    a = 2;
    break;
    
  case 3:
    a = 3;                              //работаем и в других кейсах
    break;
}

<

Но, тут прямая ошибка - он создаётся именно в этом месте. Это очень легко проверить. Возьмите не int, а что-нибудь, имеющее конструктор. Поставьте печать перед объявлением, в конструкторе и после объявления - увидите, что создаётся точно в том месте, где объявлено. Вот, пример:

struct Kaka {
	Kaka(void) {
		Serial.println("Constructor");
	}
};

void setup(void) {
	Serial.begin(57600);
	Serial.print("Go on!\r\n");
	int n = digitalRead(3);
	switch (n) {
		case 2:
			Serial.println(2);
			break;
		case 0: 
			Serial.println("Before");
			Kaka kaka;
			Serial.println("After");
			break;
		case 3:
			Serial.println(3);
			 break;
	}
}

void loop(void) {}

Результат

Go on!
Before
Constructor
After

Green пишет:

Я столкнулся с этим пару лет назад, потому и запомнил.)

Зря тогда же не разобрались. Там всё просто и, если понимать суть дела, то ничего загадочного.

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

Я тоже не разбирался. :-)  Не работает, значит на то есть у него своя сакральная правда. :-)  Няхай. 

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

А какая переменная 'а' будет использоваться в строке #17 (case 3, a=3), если декларация не будет выполнена?

Green
Offline
Зарегистрирован: 01.10.2015

Всё равно что такая:

switch (x) {
    int a;                              //определяем
  case 1:
    break;

  case 2:
    a = 2;
    break;
    
  case 3:
    a = 3;                              //работаем и в других кейсах
    break;
}
Green
Offline
Зарегистрирован: 01.10.2015

Насчёт ошибок.

#define F_CPU 16000000UL

int main() {
  int x = 1;
  switch (x) {
    case 1:
      int a = 1;
      break;
    
    case 2:
      a = 2;
      break;
  }
}
avr-gcc --version
avr-gcc (AVR_8_bit_GNU_Toolchain_3.4.2_939) 4.7.2
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
avr-gcc -Wall -Os -mmcu=atmega328p -c main.cpp -o main.o
main.cpp: In function 'int main()':
main.cpp:10:10: error: jump to case label [-fpermissive]
main.cpp:7:11: error:   crosses initialization of 'int a'
make: *** [all] Ошибка 1
sadman41
Offline
Зарегистрирован: 19.10.2016

Green пишет:

Всё равно что такая:

switch (x) {
    int a;                              //определяем
  case 1:
    break;

  case 2:
    a = 2;
    break;
    
  case 3:
    a = 3;                              //работаем и в других кейсах
    break;
}


Однако у ЕвгенияП инит происходит в том же кейсе, где и объявление произведено. Не складывается у меня картинка

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

sadman41 пишет:
А какая переменная 'а' будет использоваться в строке #17 (case 3, a=3), если декларация не будет выполнена?

Декларация не может быть выполнена или не выполнена - она неисполняема. Не выполненной может быть инициализация. Объявление же оно как скрипач - всегда в законе. Например, вот вполне корректная запись:

switch (n) {
	int a; // это место ВООБЩЕ никогда не исполняется. Объявлению это не мешает
	case 2:
		a = 2; // это та самая a из строки №2
		Serial.println(a);
		break;
	case 0: 
		a = 3;  // и это та самая a из строки №2
		Serial.println(a);
		 break;
}

 

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

Green пишет:

Насчёт ошибок.

Либо там выбрана опция "трактовать предупреждения, как ошибки", либо это самодеятельность реализации. Язык такое вполне позволяет и, как видите, GCC (который с IDE) вполне это ест. Предупреждает о странности, но ест.

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

Просто я синтаксически ожидал, что компилятор, перепрыгнув на кейс 3, проигнорирует объявление переменной. А он декларации без инициализации выносит в начало блока {}, выходит.

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

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

void setup(void) {
	Serial.begin(115200);
	if (digitalRead(3)) goto kaka;
	int n = digitalRead(5);
kaka: 
	Serial.println(n);
}
sadman41
Offline
Зарегистрирован: 19.10.2016

Тема про свич, слово за слово, как говорится...

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

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

sadman41 пишет:
Просто я синтаксически ожидал, что компилятор, перепрыгнув на кейс 3, проигнорирует объявление переменной. А он декларации без инициализации выносит в начало блока {}, выходит.

Ничего он никуда не выносит.

Ну, можно говорить, что выносит, но тогда уж всё - хоть с инициализацией, хоть без.

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

Область видимости переменной - весь блок. Но пользоваться ею можно только после объявления (здесь "после" надо буквально понимать как "ниже по тексту"). И инициализация её происходит в месте объявления!

Т.е. смотрите, программ входит в блок. Если у нас нет изощрённой оптимизации, то в момент входа в блок на стеке (или где там ещё) резервируется место под все переменные, объявленные внутри блока (в любом его месте). Но пользоваться переменой можно только начиная с точки объявления.

Почему так сложно - это пережиток тех времён, когда переменные в Си можно было объявить только в начале блока. С тех пор разрешили объявлять где попало, но делать выделение памяти на стеке где попало - очень сложно и часто неоднозначно. Потому сделано так, как сделано.

Свич же - самый обыкновенный блок (один) с метками и механизмом перехода на эти метки. Но это именно блок и метки (в стандарте они так и называются "Case labels" и это просто разновидность метки). Более того, они даже не обязаны находиться на одном блочном уровне. Метки же не обязаны! Вот смотрите, на пример, где разные кейсы находятся на разных уровнях иерархии блоков. И это не мешает им нормально работать:

void setup(void) {
	Serial.begin(57600);
	int n = digitalRead(3);
	switch (n) {
		case 0:
		{
			Serial.println("ZERO");
			break;
			
			case 1:
			Serial.println("ONE");
			break;
		}
	}
}

void loop(void) {}

а вот ещё большее извращенство, но тоже нормально работает

void setup(void) {
	Serial.begin(57600);
	int n = digitalRead(3);
	switch (n) {
		case 0:
		{
			Serial.println("ZERO");
			{
				break;
				
				case 1:
				Serial.println("ONE");
				break;
			}
		}
	}
}

void loop(void) {}

дайте HIGH на третий пин и он радостно скажет ONE

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

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

Область видимости переменной - весь блок. Но пользоваться ею можно только после объявления (здесь "после" надо буквально понимать как "ниже по тексту"). И инициализация её происходит в месте объявления!

Хочу уточнить про буквальность: т.е. в кейсе, что находится выше объявления, обращение к переменной будет ошибочно, а в кейсе пониже - уже валидным? Т.е. как понимать слово "можно" - какие последствия будут при обращении до объявления? К сожалению на телефоне канпилятора нет - проверить нет возможности, а сам я такими штуками не балуюсь - стараюсь, чтобы все было или квадратно или покрашено в зелёный цвет.

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

Green
Offline
Зарегистрирован: 01.10.2015

del

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

sadman41 пишет:
Хочу уточнить про буквальность: т.е. в кейсе, что находится выше объявления, обращение к переменной будет ошибочно, а в кейсе пониже - уже валидным? Т.е. как понимать слово "можно" - какие последствия будут при обращении до объявления?

Ещё раз. Фигурные скобки после switch - это самый обыкновенные блок. Выражения case - просто метки и не более. Никакого влияния на области видимости переменных они (кейсы) не оказывают от слова совсем. Просто блок, а внутри метки. Вот это нужно понимать чётко.

А теперь, понимая, что это самый обыкновенный блок, кейсы блоками не являются, а являются просто метками, сами ответьте на свой вопрос. Что будет если в любом блоке объявить переменную после использования? Или начать использовать до объявления? Будет сообщение про необъявленную переменную и ничего более.

Я ответил?

sadman41 пишет:

Я читал, что оптимизатор хитрым образом строит таблицу переходов и джамп на кейс 3 вполне в может быть ближе джампа на кейс 1. Так что, ежели требуется буквальная трактовка последовательности кейсов, то оптимизатор бреется.

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

Green
Offline
Зарегистрирован: 01.10.2015

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

Green пишет:

Насчёт ошибок.

Либо там выбрана опция "трактовать предупреждения, как ошибки", либо это самодеятельность реализации. Язык такое вполне позволяет и, как видите, GCC (который с IDE) вполне это ест. Предупреждает о странности, но ест.


Что значит "это самодеятельность реализации"? Командная строка перед Вами, никаких опций.

avr-gcc --version
avr-gcc (AVR_8_bit_GNU_Toolchain_3.4.2_939) 4.7.2
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
avr-gcc -Wall -Os -mmcu=atmega328p -c main.cpp -o main.o
Green
Offline
Зарегистрирован: 01.10.2015

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

Но, тут прямая ошибка - он создаётся именно в этом месте. Это очень легко проверить. Возьмите не int, а что-нибудь, имеющее конструктор. Поставьте печать перед объявлением, в конструкторе и после объявления - увидите, что создаётся точно в том месте, где объявлено. 


С конструктором не убедили. Думаю, это не чистый эксперимент.)

Green
Offline
Зарегистрирован: 01.10.2015

del

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

Green пишет:

Что значит "это самодеятельность реализации"? 

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

Green пишет:

Командная строка перед Вами, никаких опций.

И что, я не знаю какие там опции по умолчанию. У меня в IDE Ваш пример нормально скомпилировался. С предупреждениями, но без ошибок. И с точки зрения языка это правильно.

Green пишет:

С конструктором не убедили. Думаю, это не чистый эксперимент.)

Читайте стандарт языка, может тогда убедитесь :-)

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

sadman41,

на самом деле, там всё ещё смешнее. Как я уже сказал, главное понимать, что фигурные скобки после switch - это самый обыкновенные блок в начале которого есть пачка if'ов c goto. Выражения case - просто метки и не более. Если это понимать, то можно смело предполагать как именно он сработает в той или иной ситуации. ну, вот, например, посмотрите на эту шикарную конструкцию (задача сформулирована в комментариях) - и работает ведь!

//
// ОСТОРОЖНО - ГОВНОКОД !!!
// Начинающим не смотреть вовсе!
//	все трюки выполнены профессионалами - не пытайтесь повторить самостоятельно
//
void setup(void) {
	Serial.begin(57600);
}
void loop(void) {
	int n = random(0, 3);
	Serial.print("n="); Serial.println(n);
//
//	ЗАДАЧА:
//	Если n==1 - вывести "A"
//	Если n==2 - вывести "B"
//	Если n==0 И на пине №3 HIGH - вывести "A"
//	Если n==0 И на пине №3 LOW - вывести "B"
//
	switch (n) {
		case 0:
			if (digitalRead(3)) {
				case 1:
					Serial.println("A");
			} else {
				case 2:
					Serial.println("B");
			}
	}
	delay(500);
}

Надеюсь, мисрасты, структурасты и прочие сектанты этого не увидят, иначе быть мне съеденным под соусом из дерьма. Но, как видите, оператор switch - это просто кладезь возможностей для истинно-творческого человека :-)))

P.S. если кто-то хочет сказать мне, что за переходы внутрь блоков if и else, минуя собственно проверку условия, мне предстоит гореть в аду - станьте в очередь, ибо Вы не первый. А что до ада, так там будет отличная компания - Кнут, Брукс и др., а в раю что ... с Виртом что-ли тёрки тереть?

Green
Offline
Зарегистрирован: 01.10.2015

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

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

И что, я не знаю какие там опции по умолчанию. У меня в IDE Ваш пример нормально скомпилировался. С предупреждениями, но без ошибок. И с точки зрения языка это правильно.

Тут скорее НАШ IDE отходит от стандарта. Возьмём GCC4.9.2 для старшего брата:


int main() {
  int x = 0;
  switch (x) {
    case 1:
      int a = 1;
      break;
    
    case 2:
      break;
  }
}
D:\arc\2020\200619\Dev_cpp\1.cpp In function 'int main()':
9 10 D:\arc\2020\200619\Dev_cpp\1.cpp [Error] jump to case label [-fpermissive]
6 11 D:\arc\2020\200619\Dev_cpp\1.cpp [Error] crosses initialization of 'int a'
6 11 D:\arc\2020\200619\Dev_cpp\1.cpp [Warning] unused variable 'a' [-Wunused-variable]
28 D:\arc\2020\200619\Dev_cpp\Makefile.win recipe for target '1.o' failed
 
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Green,

я уже второй день Вам говорю, что в стандарте НЕТ запрета на обход инициализации при помощи перехода на метку операторами goto или switch. Да, это идиотизм, но это незапрещённый идиотизм. Вот, например, запрет входить внутрь блока catch при помощи goto/switch есть - да, это запрещено. А обходить инициализацию - нет такого запрета.

Поэтому, если Вы заявляете, что 

Green пишет:

Тут скорее НАШ IDE отходит от стандарта. 

то уж будьте последовательны - и укажите какой именно параграф стандарта запрещает такие переходы и (стало быть) нарушается компилятором GCC, который поставляется с IDE.

Утверждаете, что такой запрет есть - покажите.

Green
Offline
Зарегистрирован: 01.10.2015

Нет, я не знаю стандартов и у меня нет желания с ними разбираться. Но я вижу что более старые версии IDE (другие версии GCC) дают ошибку ввиду невозможности инициализации локальной переменной в блоке switch без задания дополнительного блока, в отличие от свежих версий IDE (у меня 1.8.12), которые ограничиваются предупреждением. При этом Вы считаете что все эти другие версии неправильные, что весьма странно, не находите?
Касательно наших с Вами расхождений, как я понимаю, они в сравнении блока switch с обычным блоком. Вы утверждаете что разницы нет, тогда как разница очевидна. И это видно на примерах. И я тоже уже второй день именно об этом Вам говорю.

 

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

Green пишет:
Вы утверждаете что разницы нет, тогда как разница очевидна. И это видно на примерах. И я тоже уже второй день именно об этом Вам говорю.
Странно, а я и не понял, что Вы мне именно это говорите. Да, и разница для меня совершенно неочевидна, вернее сказать, я её в упор не вижу, т.к. её нет. И ни в одном примере я этой разницы не заметил.

Давайте Вы попробуете чётко и без шелухи ответить именно на этот вопрос. В чём же всё-таки разница между switch и любым другим блоком? Ну, не считая синтаксического сахара типа того, что пачку проверок и goto компилятор сам вставит, а другом блоке пришлось бы руками писать.

Green
Offline
Зарегистрирован: 01.10.2015

В который раз?
Я в упор не вижу (не хочу видеть) никаких проверок и никаких goto.
Разница в том что мы не можем инициализировать локальную переменную.
В одной версии компилятора получаем ошибку, в другой предупреждение.
Это нужно рисовать или и так понятно?

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

Грин, для меня всё ясно стало в #34.  А ты пошто споришь?