Apparently C allows a variable's initialization to refer to the variable being declared, as in
int x = x; |
I guess the reasoning is that by the time the compiler reaches the initialization on the right side of the "=", the declaration on the left side has allocated memory for the variable, so the variable may be referenced.
This language feature may have its uses, but it can also lead to subtle bugs. I just fixed a bug that boiled down to the fact that the following compiled and ran:
- (void)wtf:(NSString *)ping { NSString *pong = [pong self]; NSLog(@"%@", pong); } |
The first line of the method should of course have been
NSString *pong = [ping self]; // ping, not pong |
but due to copy-paste and variables looking alike, I didn't spot the typo until our QA guy reported a crash and his stack trace led me to this method.
By coincidence, the code had worked correctly for me every time in development. In the debugger, I printed the value of pong on entry, and it happened to be the value of ping, presumably because that was the leftover value that happened to be on the stack.
I don't think this is a compiler bug. I believe the C standard allows the incorrect code I wrote, for two reasons. First, I created a scratch project and compiled the above wtf:
method verbatim with GCC 4.2, LLVM GCC 4.2, and LLVM compiler 1.7. Second, I vaguely remember someone posting a trick for creating variables with unique values for use as the "context" passed around by KVO. I think the trick used this feature of C.
Update: I think the KVO trick was something like
void *x = &x; |
I guess there no reason to disallow this, since it's equivalent to
void *x; x = &x; |
So now that I think about it, my bug wasn't really related to initialization. I could just as easily have made my mistake this way:
- (void)wtf:(NSString *)ping { NSString *pong; pong = [pong self]; NSLog(@"%@", pong); } |
I tried compiling f(int x) {int x = x+1; return x;} and it wouldn't compile.
foo1.c:1: error: 'x' redeclared as different kind of symbol foo1.c:1: error: previous definition of 'x' was here
I tried compiling f() {int x = x; return x;} and it did compile. I guess my comment would be, to anyone who wrote that, "If it hurts when you do that, stop doing it." There are times when a variable might legitimately take a new value after it has been initialized, but there is no mechanism for detecting an uninitialized value. I thought -Wall would, but it didn't.
My problem was that I'd written
f(int y) {int x = x; return x;}
when I'd meantf(int y) {int x = y; return x;}
. In my case it was a typo, and because the compiler was perfectly happy with it, and it happened to work for me, I never knew there was a bug.But I can imagine wanting to use this language behavior on purpose. There's the "create a program-wide unique constant" case I mentioned (I added it later, so you may not have seen it). Also, I can imagine wanting to initialize a struct such that one of its members is a pointer to itself.