Every now and then, I run across a bug in a C program that makes me say “oh, one of those again?” For instance, a very common one is when you want to compare two values using equal (“==”) but you leave one off and do an assignment by mistake (“=”):
if (a = 10) { DoSomething(); }
When you do this, the result is the value, thus “a = 10” resolves to 10, so it’s like saying “if (10) …”. It would work for any non-zero value, since “if (0)” would not run. A fun bug to find!
Most modern C compilers will emit a warning about that, because they know it’s probably not what the programmer intended.
But a lot of crappy small embedded compilers don’t. And since I work with a lot of small embedded compilers, I stumble across bugs like this from time to time.
I found a new one to add to my list, this time using a logical “and” comparison (“&&”) versus an accidental bitwise “and’ (“&”). Consider this:
if ( (a==100) & (b==42) ) { DoSomething(); }
The intent of that code was to only DoSomething() if a was 100 AND b was 42. But, there was only one ampersand, so it was actually taking the result of “a==100” (either true or false) and mathematically ANDing it to the result of “b==42” (either true or false).
If a was 100 and b was 42, it was doing this:
if ( (true) and (true) ) { DoSomething(); }
And that worked, since “true” resolves to a 1, and “1 & 1” is 1. (My grade school math teacher never taught it that way…)
But, clearly this was a bug and it should have been the logical AND (“&&”).
Which made me look at the generated code and see what the difference is. And the difference is that using the bitwise AND generates far more code because it has to save the results of two comparisons then AND them together and check that result. It was basically doing this:
result1 = (a == 100); // true or false result2 = (b == 42); // true or false if (result1 & result2) { DoSomething(); }
So sure, it works, but it generated much larger code and did much more work and thus was both larger and slower than doing the desired logical AND (“&&”).
And that’s all I have to say about that, today.
Until next time…
I always struggle with those, have to search online to find out which one to use.
In this case you “only” got sub par code generation, but when you want to do a logical and between a comparison result and just any variable (i.e. “is it anything else than zero?”) this really bites you as C unfortunitely returns 1 and not -1 as “true”, and thus will any even value in the variable make the bitwise and return zero. Had C returned -1 as “true” from logic operators, this wouldn’t had been a problem. Sure, using the result of a logic operation to add/subtract one does look ugly and counterintuitive when logic operators return -1, bud you don’t even need separate bitwise and logical and/or operators to construct a usable programming language. This is one of the rare cases where I find old plain basic on 8-bit computers better than C. Perhaps even the only case tbh.
Maybe you could (always or periodically) run your code through GCC too, and set up some IFDEFs to just give GCC whatever it needs to not produce errors due to the embedded specific stuff, just to have GCC dig out all those potential bugs that it will warn about?
The GCC thing is a good idea. I actually developed routines as a console app under GCC and then make them work on the embedded system. But none of the others I work with do that.
Pingback: IF AND/OR THEN versus IF THEN IF – part 1 | Sub-Etha Software