Compiler problem with local variable definition inside switch

Hi everybody,

I'm on the verge of madness after hours of debugging for a problem that's very stupid but required a lot of times to be solved because when happened Arduino went mad and there is no useful println instruction when Arduino is mad.

The problem is that the compiler allows me to write and load a code like this one

int state = 0;

int test(){
  switch(state) {
  case 0:
    int omg = 666;
    Serial.println(omg);
  break;
  default:
    Serial.println("wrong!");
    state = 0;
  }
}

void setup() {
  Serial.begin(9600);
    Serial.println("initted!");
}

void loop() {
  state = 1;
  test();
}

without warnings, but during execution nothing works.

Yes, I know that it can be solved with a couple of {} into the switch statements
Yes, the sketch was like 1k line of codes I must block commented before finding the solution

Greetings from a mental health hospital

Tested with IDE 1.8.13, Arduino Nano, Atmega328P (old bootloader)

Turn on the warnings - you have a scope issue for “omg” - yes, a pair of braces fixes it.

int test(){
  switch(state) {
  case 0: {
    int omg = 666;
    Serial.println(omg);
  } 
  break;
  default:
    Serial.println("wrong!");
    state = 0;
  }
}

Otherwise:

home/*****/SCOPEISSUE/SCOPEISSUE.ino: In function 'int test()':
/home/****/SCOPEISSUE/SCOPEISSUE.ino:9:3: warning: jump to case label [-fpermissive]
   default:
   ^~~~~~~
/home/****/SCOPEISSUE/SCOPEISSUE.ino:6:9: note:   crosses initialization of 'int omg'
     int omg = 666;
         ^~~
/home/*****/SCOPEISSUE/SCOPEISSUE.ino:13:1: warning: no return statement in function returning non-void [-Wreturn-type]
 }
 ^

Quick explanation: “omg” is in scope in both cases, but is only initialised in the first case.
This produces undefined behavior, as you have found.

Excuse me, sir, I can't find any scope problem in my code.

I always use omg only after initialization - no omg printed in the default case.

I always used local variables into a switch in that way, the only mention is that if I want to use the same name in another case I must bracket the code. Bracketing was an option if I didn't use the same name. Once all errors was described in a almost understandable way during compiling phase, and compiled code was always stable if one doesn't start to play with pointers.

You know the fix.
Use it.

brazoayeye:
Excuse me, sir, I can't find any scope problem in my code.

The compiler disagrees with you, and in this situation, the compiler wins.

I can't find any scope problem in my code

Did you not understand the "quick explanation"?

In the Arduino IDE, enable File > Preferences > Compiler Warnings > "ALL" and you should see the warnings. The IDE runs the compiler with the -fpermissive option, which causes some errors (such as this) to instead be flagged as warnings. This was implemented years ago to maintain compatibility with older libraries when an update to the compiler started flagging certain things as errors that were previously only warnings.

Note that the following will compile without error or warnings. The problem seems to only occur when the variable is declared with an initialization.

int state = 0;

void test() {
  switch (state) {
    case 0:
      int omg;
      omg = 666;
      Serial.println(omg);
      break;
    default:
      Serial.println("wrong!");
      state = 0;
  }
}

void setup() {
  Serial.begin(9600);
  Serial.println("initted!");
}

void loop() {
  state = 1;

That's the point, compiler didn't show any error. I found the problem after like 2h, that's not good.

If warnings are critical to the software, they should be errors.
If warnings are critical to the software and they pretend not to be errors, they should be printed by default.

@jremington : This is the first time I see this behaviour, so even if an explanation exists another should exist to explain why other times it worked. Probably it's related to compiler dirty job, and I don't know if I want to go deeper. I don't know assembly, but even if a variable is in scope and not used, I think it should not interfere with code flow because never called. Isn't it?

@david_2018: wtf, your code also runs without problems. This could explain why in the past I used local vars into switch without problem, but emphasize the compiler guiltiness

The technical answer (as posted on https://stackoverflow.com/a/11578973/4304 ) is the following:

The C++ standard says:

It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps from a point where a local variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has POD type (3.9) and is declared without an initializer.

The cases in switch are considered as a “jump”.

The use of the -fpermissive option by the IDE has been debated apparently since it was implemented, and the desire to maintain compatibility with code that is no longer maintained is seen as a priority.

It may never be possible to write a compiler that will find all of your mistakes.

That is one reason why these forums exist, and you very quickly got the answer you needed to hear.

When using the Arduino IDE, turn on warnings and heed them.

I have 1.8.4, it complained about omg and then default.

I changed omg to a global variable, and that seems to fix it:

int state = 0;
int omg;

int test(){
  switch(state) {
  case 0:
    omg = 666;
    Serial.println(omg);
  break;
  default:
    Serial.println("wrong!");
    state = 0;
  }
}

void setup() {
  Serial.begin(9600);
    Serial.println("initted!");
}

void loop() {
  state = 1;
  test();
}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.