Odd behavior of conditional operator "?" and logical negation "!"

Status
Not open for further replies.

khtos

Active member
Hello, community!
Firstly, I post this message on Visual Micro support forum, but they redirected me here.
I have noticed a strange behavior of the conditional operator “?” while working with bool variables.
First of all, the bool variable was initialized incorrectly. I restored it from an uninitialized EEPROM, so the variable contained 0xff instead of 0 or 1.
The attached program just demonstrates the issue.
The program was compiled for teensy 4.0 and teensy 4.1 and show the same results for both boards.
Here is the list of the program with comments:
Code:
bool     Bool_var;
uint8_t  temp = 0x7f;
uint8_t  Key = 0;
uint8_t  OldKey = 0;

void setup() {
	Serial.begin(115200);
	while (!Serial) { ; }

	*(uint8_t*)&Bool_var = 0xff;	// initially here was restoring a variables from EEPROM, which wasn't initialized!

	pinMode(0, INPUT_PULLUP);	// hardware button connected to pin 0
}

// the loop function runs over and over again until power down or reset
void loop() {

	Key = digitalReadFast(0);				// reading the hardware button and try to invert the bool variable on each pressing
	if ((Key == 0) && (OldKey == 1)) {			//
		//Bool_var = Bool_var ? false : true;		// initially i saw issue in this expression, but during the studying
														// of the problem, I changed the code to one that is not commented out.
		temp = Bool_var ? 0 : 1;			// if I change this expression, to use other values, not just 0 and 1,
																// everything starts to work perfectly.
		//temp = Bool_var ? 0 : 5;                      // For example, this expression works fine.
		Bool_var = !Bool_var;
		//Bool_var = (bool)temp;                        // this string gives the same result as previous string

		Serial.printf("temp=%i; Bool_Var=%i; ", temp, Bool_var);    // output results to terminal
		if (Bool_var)  Serial.printf("Bool_Var=true;\n");
		else           Serial.printf("Bool_Var=false;\n");
	}
And here is a result from terminal:
Code:
Opening port Port open
temp=254; Bool_Var=254; Bool_Var=true;
temp=255; Bool_Var=255; Bool_Var=true;
temp=254; Bool_Var=254; Bool_Var=true;
temp=255; Bool_Var=255; Bool_Var=true;
temp=254; Bool_Var=254; Bool_Var=true;
temp=255; Bool_Var=255; Bool_Var=true;
As you can see, the Bool_Var changes only its last bit and as a result, keeps the “true” value. Moreover, incorrect values are assigned to the “temp” variable, which, in principle, should not happen!
I know, there are many ways to get around this problem, but as I know everything should work as it is!
Of course, it doesn't bother me much, but I wonder if anybody met this behavior? Is it really a glitch or I don't see something?
 
Wow that's interesting.
Look here: https://godbolt.org/z/fPbavG8W7

It does invert the last bit and returns the value.. but it does it
- with the latest version of GCC, too
- with other targets, too
- on any optimization level but 0
- clang does it, too
- msvc? I don't know other assembler languages that good.. not sure?
 
...but I wouldn't say it's a bug.
You're lying to the compiler.
You do a strange pointer operation.

a) that value is not bool and b) the compiler perhaps could know about that, but does not use this information. It can rely on Bool_var having allowed values.

I *guess* the language allows it to just invert the last bit.
 
But, does the language allow to assign wrong values to temp, which is uint8?
I initialized it with 127, and than assign 0 or 1, depend on Bool_var value. But as a result of “?” we get 254/255 assigned to temp!
 
You're writing 0xff to the adress where Bool_var resides. That seems to be allowed.

Hm, I only see the invert of the last bit (LSB). Can you post the code with 127? (But it's too late now, here. I will read the answer tomorrow)


I hope someone knows that stuff better than me, and can explain it.
However, the operation on the LSB is OK, I think.

Last interesting thing:
Code:
[COLOR=#000000][FONT=Consolas][COLOR=#0000ff]int[/COLOR][COLOR=#000000] foo([/COLOR][COLOR=#0000ff]bool[/COLOR][COLOR=#000000] b) {[/COLOR][COLOR=#0000ff]return[/COLOR][COLOR=#000000] ([/COLOR][COLOR=#0000ff]int[/COLOR][COLOR=#000000]) ( (b==[/COLOR][COLOR=#0000ff]true[/COLOR][COLOR=#000000]) ? [/COLOR][COLOR=#098658]0[/COLOR][COLOR=#000000] : -[/COLOR][COLOR=#098658]1[/COLOR][COLOR=#000000]); }[/COLOR][/FONT][/COLOR]

results in
Code:
[COLOR=#000000][FONT=Consolas][COLOR=#0000ff]subs[/COLOR][COLOR=#4864aa] r0[/COLOR][COLOR=#000000], [/COLOR][COLOR=#4864aa]r0[/COLOR][COLOR=#000000], [/COLOR][COLOR=#098658]#1[/COLOR][/FONT][/COLOR]

Clever ;)


 
Last edited:
Once you do anything with undefined behavior, ANY subsequent behavior become legal. Crash, random values, etc.
 
Code:
	*(uint8_t*)&Bool_var = 0xff;	// initially here was restoring a variables from EEPROM, which wasn't initialized!
That's why you shouldn't just blindly cast values like that!

I presume the original was something like
Code:
	Bool_var = (bool) EEPROM.read (addr) ;
Rather than something typesafe and clear like:
Code:
	Bool_var = EEPROM.read (addr) != 0x00 ;
Of course C/C++ offers many ways to shoot yourself in the foot, its best to be circumspect and nip them in the bud.

Alas the EEPROM.get method doesn't handle this either, although I think it could be specialized for bool.
 
Status
Not open for further replies.
Back
Top