I recently resolved a bug that has been occupying my mind rent free for a while.
I tried to initialize a variable within a case statement. Apparently, this does not compile in standard C++ (I haven't tried it), but Arduino's build works perfectly fine without even a warning. Strangely, when I upload the sketch and run it, case 50 works fine, but cases 51 and 52 are completely ignored.
Why is that? And why doesn't Arduino's build command not raise it as an error?
switch(incomingByte) {
case 48:
glow_white();
break;
case 49:
flash_red();
break;
case 50:
int ava = 0; // this was the problem
do {
rainbow();
ava = Serial.available();
} (while ava == 0);
break;
case 51:
// Machine done.
// Shows red instead
glow_green();
break;
case 52:
// Machine off
led_off();
break;
default:
glow_white();
}
It doesn't compile for me. I get an "error: expected 'while' before '(' token" from your malformed while statement. I also get the expected "crosses initialization of 'int ava'" error.
Please post your full sketch.
Note that you can initialize a variable in a case if you make it local to the case by wrapping the case code in braces.
variable declaration needs to be enclosed by a scope either globally or locally. A switch case label does not have a scope.
The following code is valid:
UKHeliBob:
There has been a change in behaviour between versions of the IDE as I am used to seeing the "crosses initialization" error
I just tried compiling that code with the oldest version of the Arduino IDE I have installed, 1.0.5-r2 (which bundles avr-gcc 4.3.2) and it still compiles without errors.
The earliest version I have installed is much later than that and compiles the first code in reply #3 without error so the "crosses initialization" error must be caused by some other combination of circumstances
After fixing the obvious typo and adding enough of the missing code to attempt a compile:
void setup()
{
Serial.begin(115200);
}
void glow_white(){};
void flash_red(){};
void rainbow(){};
void glow_green(){};
void led_off(){};
void loop()
{
if (Serial.available())
{
byte incomingByte = Serial.read();
switch (incomingByte) {
case 48:
glow_white();
break;
case 49:
flash_red();
break;
case 50:
int ava = 0; // this was the problem
do {
rainbow();
ava = Serial.available();
} while (ava == 0);
break;
case 51:
// Machine done.
// Shows red instead
glow_green();
break;
case 52:
// Machine off
led_off();
break;
default:
glow_white();
}
}
}
I get the following warnings, which may explain why case 51 and case 52 don't work:
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino: In function 'void loop()':
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino:32:12: warning: jump to case label [-fpermissive]
case 51:
^~
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino:26:13: note: crosses initialization of 'int ava'
int ava = 0; // this was the problem
^~~
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino:37:12: warning: jump to case label [-fpermissive]
case 52:
^~
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino:26:13: note: crosses initialization of 'int ava'
int ava = 0; // this was the problem
^~~
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino:41:7: warning: jump to case label [-fpermissive]
default:
^~~~~~~
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino:26:13: note: crosses initialization of 'int ava'
int ava = 0; // this was the problem
^~~
Sketch uses 1474 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 184 bytes (8%) of dynamic memory, leaving 1864 bytes for local variables. Maximum is 2048 bytes.
This eliminates the warnings:
case 50:
{
int ava = 0; // this was the problem
do {
rainbow();
ava = Serial.available();
} while (ava == 0);
}
break;
As does this:
case 50:
do {
rainbow();
} while (Serial.available() == 0);
break;
Sorry for the typos, I tried to just provide the minimal amount of code since my question was more philosophical than trying to solve a problem but I should have assumed that somebody would actually try compiling it.
While I have the Arduino IDE and packages installed (1.8.10), I use arduino-cli (0.7.0) on command-line Linux to build and uploaded sketches. It must be suppressing warnings, though I am not sure why the underlying gcc (avr-gcc-7.3.0) does not raise the compilation error.
For anyone that was curious, this is the full sketch. I have since fixed it, but changed it back to the failing state:
You need to enclosed your variable declaration with initialization inside a {}.
The reason for this is since C/C++ allows cases to follow through, it is possible that you can skip a case where you declare your variable and use it in the next case. For example:
int test = 1;
switch (test) {
case 0:
int num = 3;
Serial.println(num);
break;
case 1:
Serial.println(num);
break;
}
The above code is not allowed because "num" is still in-scope in case 1, but we don't know the value of num since we skipped its initialization in case 0.
That being said, the following 2 situations are valid:
switch (test) {
case 0:
int num;
num = 3;
Serial.println(num);
break;
case 1:
num = 4; //if you remove this, code still compiles but value could be(?) garbage
Serial.println(num);
break;
}
switch (test) {
case 0: {
int num = 3;
Serial.println(num);
break;
}
case 1:
break;
}
Bytemining:
Sorry for the typos, I tried to just provide the minimal amount of code since my question was more philosophical than trying to solve a problem but I should have assumed that somebody would actually try compiling it.
Bytemining:
why doesn't Arduino's build command not raise it as an error?
Because they use the -fpermissive compiler flag in Arduino AVR Boards' compilation recipes. This downgrades it from an error to a warning (but you'll only see the warning if you have them enabled).
UKHeliBob:
Windows 10, IDE 1.8.10, Nano 33 IOT or a Nano
There has been a change in behaviour between versions of the IDE as I am used to seeing the "crosses initialization" error
The -fpermissive option was added in Arduino AVR Boards 1.6.12 (which is bundled with Arduino IDE 1.6.10. The reason is they updated to a newer version of avr-gcc at that time, which upgraded some warnings to errors and Arduino didn't want to break code that previously worked. Unfortunately, -fpermissive also downgraded some things that had previously been errors to warnings as well:
Arduino SAMD Boards does not use -fpermissive in its compilation recipes, so the same code that will only generate a warning when compiled for an AVR board will generate an error when compiled for your Nano 33 IoT.
In the Arduino IDE, you can enable warnings at File > Preferences > Compiler warnings. Although each hardware package can define them as the author chooses, in the official Arduino hardware packages (and many 3rd party packages as well), the "More" setting is -Wall and the "All" setting is -Wall -Wextra.
arduino-cli compile has a --warnings option that uses the same level names as the IDE.
The official Arduino hardware packages don't use -Werror, but the ESP32 core authors did use -Werror=all in the "More" and "All" warning levels. I think it was a good idea as a way to enforce higher code, but I have also seen it cause some suffering among beginners who turned warnings on, then encountered errors in code written by people who didn't bother to turn their warnings on.
You can add -Werror flags to any package by creating a file named platform.local.txt in the same folder as the package's platform.txt and adding this text to the file: