Monday 26 January 2009

Not all assignments are equal: = != ==

C, for all its brevity, is a minefield for the unwary.

One of the more irritating gotchas in the lexicon is that:

if( x = 10 )

is perfectly correct C, and will compile and run perfectly happily. The preprocessor will not squint at it, the compiler will look the other way, the linker will care not a jot. And yet it is almost certainly wrong, as it is an assignment rather than a test for equality (that is, it sets x to 10, rather than testing if x is already equal to 10).

What was probably intended here was:

if( x == 10 )

Grizzled veterans (by which I mean those who've been bitten often enough by this particular gotcha) avoid the whole issue by writing:

if( 10 == x )

This is a neat approach, as this code will not compile if you accidentally try to perform an assignment:

if( 10 = x ) // compiler error

This is a good sign in an interview candidate, as it displays both an awareness of the problem, the natural cunning to work around it, and the self-awareness to admit that they won't always get it right.

I still fall prey to this occasionally, despite being fully aware of the issue. Perhaps once every two years, while tracing some random weirdness in my code, I'll be stepping through the source in the debugger when I see the dreaded accidental assignment of doom. At this point I generally curse, make a mental note to kick the cat when I get home, and quickly fix the offending line before anyone can see it.

The problem is that the part of my brain that does maths rebels at the 'backwards' statement 'if 10 equals x'. It just looks ugly and wrong, despite being the 'smart' thing to do. I do actually do it, but I have to force myself to, and it always feels a bit clunky. And because it feels a bit clunky, I sometimes submit to my natural tendency to write it the other way round.

In my daily work I tend to rapidly context switch between C, C++, Delphi, and Perl. Delphi is particularly nasty in this respect, as in it '=' is the test for equality. I suspect that I generally make this mistake on switching from Delphi to C/C++.

1 comment:

Nigel Jones said...

I agree that this is one of those things that can drive you up the wall. Personally I always use Lint - as it will complain very loudly if it sees an assignment when an equality test is most likely intended.

The other construct that I find myself doing too often is this:

int foo[10];

bar(&foo);

Again it's perfectly legal. Fortunately Lint again rides to the rescue.