Declaring variables in "case" blocks?

R

Robbie Hatley

Greetings, group. I just found a weird problem in a program where
a variable declared in a {block} after a "case" keyword was being
treated as having value 0 even though its actual value should
have been something else. An extremely stripped-down version:

int Function (int something)
{
switch(something)
{
case WHATEVER:
{
int DumVar = 72;
// Some code uses DumVar. It seems to see the value
// of DumVar as being 0 ! What the heck???
break;
}
}
}

Is there anything wrong with that? Does it violate some rule
of C that I'm missing? It's almost as if the first time I
try to read Dumvar in the code, it's being redeclared (as int),
redefined, and reinitialized to 0.

When I moved the declaration of DumVar up to the top of the
function, it now works correctly:

int Function (int something-)
{
int DumVar = 72;
switch(something)
{
case WHATEVER:
{
// Code here now sees the value of DumVar as being 72
break;
}
}
}

So am I just missing something, or is my compiler buggy?

(I'm using National Instruments "CVI" compiler for this;
I haven't tested this on a more common compiler such as GCC.)
 
K

Keith Thompson

Robbie Hatley said:
Greetings, group. I just found a weird problem in a program where
a variable declared in a {block} after a "case" keyword was being
treated as having value 0 even though its actual value should
have been something else. An extremely stripped-down version:

int Function (int something)
{
switch(something)
{
case WHATEVER:
{
int DumVar = 72;
// Some code uses DumVar. It seems to see the value
// of DumVar as being 0 ! What the heck???
break;
}
}
}

I think you've stripped down your code so far that you've eliminated
the error. The above is valid, and code that refers to DumVar will
see its value as 72. You'd have been better off posting a small
compilable program, so you could confirm that the problem is still
there.

But here's an example that illustrates the problem you're having:

#include <stdio.h>
int main(void)
{
int x = 42;
switch(x) {
int DumVar = 72;
case 42:
printf("ok, x = %d, DumVar = %d\n", x, DumVar);
break;
default:
printf("Huh?\n");
break;
}
return 0;
}

The output I get is:

ok, x = 42, DumVar = 1628438944

after a compiler warning:

c.c:6: warning: unreachable code at beginning of switch statement

The problem is that control jumps directly from the 'switch' to the
appropriate 'case' label, in this case to 'case 42:'. After that,
DumVar is visible, but you've skipped over its initialization.

You can safely declare variables within a block following a case
label, but not within a block that's the entire body of the switch
statement.

For more fun with switch statements, see question 20.35 in the
comp.lang.c FAQ, <http://www.c-faq.com/> (Duff's Device).
 
R

Robbie Hatley

Keith Thompson said:
I think you've stripped down your code so far that you've eliminated
the error.

Maybe so.
The above is valid

That's what I was wondering about. Thanks.
and code that refers to DumVar will see its value as 72.

Theoretically, yes.

In practice (in my program, with my compiler), I was seeing 0,
which is why I was so puzzled.
You'd have been better off posting a small compilable program,
so you could confirm that the problem is still there.

I (mostly) did. If you #define WHATEVER, then call Function()
from main(), it will compile and run.

Hmmm... let me try that.

Ok, I just compiled and ran my stripped-down version here,
with a sprintf() of DumVar to a char buffer, and printed the buffer
in a popup message box. It displays as "72". Oops. Problem went
away. Weird.

I'm truly perplexed. I'll have to try to reproduce this bizarre
bug again.
But here's an example that illustrates the problem you're having:

#include <stdio.h>
int main(void)
{
int x = 42;
switch(x) {
int DumVar = 72;
case 42:
printf("ok, x = %d, DumVar = %d\n", x, DumVar);
break;
default:
printf("Huh?\n");
break;
}
return 0;
}

Nice try, but that's not my problem. I was careful to put
the variable declaration at the top of a {block}, just under a
CASE label, like so:

#include <stdio.h>
int main(void)
{
int x = 42;
switch(x) {
case 42:
{
int DumVar = 72;
printf("ok, x = %d, DumVar = %d\n", x, DumVar);
break;
}
default:
printf("Huh?\n");
break;
}
return 0;
}

and I was getting "DumVar = 0". Definitely wasn't a large
value (uninitialized RAM garbage) as you're getting here:
The output I get is:

ok, x = 42, DumVar = 1628438944

after a compiler warning:

c.c:6: warning: unreachable code at beginning of switch statement

The problem is that control jumps directly from the 'switch' to the
appropriate 'case' label, in this case to 'case 42:'. After that,
DumVar is visible, but you've skipped over its initialization.

Yep! I've seen that one bite even highly-experienced co-workers.
Since your declaration is never executed, your compiler declares
the variable as int for you, allocates some memory, but doesn't
initialize it to anything. So it's value could be anything from
-(2^31) to +(2^31)-1. (Assuming 32-bit int, 2's comp. rep.)
You can safely declare variables within a block following a case
label, but not within a block that's the entire body of the switch
statement.

Well, you could... but the code will never execute.
For more fun with switch statements, see question 20.35 in the
comp.lang.c FAQ, <http://www.c-faq.com/> (Duff's Device).

Whoa! It's actually legal to jump into the middle of a do loop
that way?! Cool machine!
 
F

Flash Gordon

Robbie Hatley wrote, On 31/07/07 21:20:
I (mostly) did. If you #define WHATEVER, then call Function()
from main(), it will compile and run.

Hmmm... let me try that.

Ok, I just compiled and ran my stripped-down version here,
with a sprintf() of DumVar to a char buffer, and printed the buffer
in a popup message box. It displays as "72". Oops. Problem went
away. Weird.

<snip>

This is why you should always reduce your code to a small compilable
example that exhibits the problem (obviously, if the problem is it not
compiling then it does not need to be compilable!). There is no point
other people trying to debug a problem in code that does not exhibit the
problem.
 
K

Keith Thompson

Robbie Hatley said:
Maybe so.


That's what I was wondering about. Thanks.


Theoretically, yes.

In practice (in my program, with my compiler), I was seeing 0,
which is why I was so puzzled.

You weren't seeing 0 with the code that you posted. Without seeing
code that does display 0, I can't make any definitive comments.
I (mostly) did. If you #define WHATEVER, then call Function()
from main(), it will compile and run.

Hmmm... let me try that.

Ok, I just compiled and ran my stripped-down version here,
with a sprintf() of DumVar to a char buffer, and printed the buffer
in a popup message box. It displays as "72". Oops. Problem went
away. Weird.

There's nothing weird about it. The code you posted doesn't exhibit
the error.

You need to create your own complete stripped-down program, and run it
yourself to exhibit the error, *before* you post it here.
I'm truly perplexed. I'll have to try to reproduce this bizarre
bug again.

Please do.

Do you still have the original code that exhibited the problem? It's
probably too big to post here, but be sure you save a copy of it so
you can use it as a basis for creating something that you can post.
Nice try, but that's not my problem. I was careful to put
the variable declaration at the top of a {block}, just under a
CASE label, like so:

#include <stdio.h>
int main(void)
{
int x = 42;
switch(x) {
case 42:
{
int DumVar = 72;
printf("ok, x = %d, DumVar = %d\n", x, DumVar);
break;
}
default:
printf("Huh?\n");
break;
}
return 0;
}

and I was getting "DumVar = 0". Definitely wasn't a large
value (uninitialized RAM garbage) as you're getting here:

Most likely you're mistaken, or perhaps you're having some other
problem that's not related to the switch statement.
Yep! I've seen that one bite even highly-experienced co-workers.
Since your declaration is never executed, your compiler declares
the variable as int for you, allocates some memory, but doesn't
initialize it to anything. So it's value could be anything from
-(2^31) to +(2^31)-1. (Assuming 32-bit int, 2's comp. rep.)


Well, you could... but the code will never execute.

The initializer, if any, will be skipped -- but the variable will
exist. This is not a good idea, though, since you can always declare
it in an outer scope.
Whoa! It's actually legal to jump into the middle of a do loop
that way?! Cool machine!

It's the language, not any particular machine. Duff's Device is legal
and portable (though there's some controversy about this).

A switch statement is a lower-level construct than it appears to be.
It's really nothing more than a computed goto within a single
statement (which can be a block). The error of skipping an initializer
in a switch statement is really no different than this:

#include <stdio.h>
int main(void)
{
goto LABEL;
{
int x = 42;
LABEL:
printf("x = %d\n", x);
}
return 0;
}

As for the problem you're having in your code, no offense, but I don't
believe that it's as you describe it. If you can post an actual
program that exhibits the problem (by "actual program" I mean
something that anyone else can copy-and-paste, compile, link, and
execute, with no modifications), then we'll be glad to look at it.
But all we've seen so far is code that doesn't exhibit the problem,
and code that does exhibit a problem but for reasons different from
what you describe.

It's possible that you've run across either an obscure corner of the
language that I'm not thinking of, or a compiler bug, but that's not
the way to bet.
 
R

Robbie Hatley

Keith Thompson said:
As for the problem you're having in your code, no offense,

None taken. :)
but I don't believe that it's as you describe it.

I didn't save a copy of the version that had the problem,
but I did manually reconstruct it by moving the variable
declarations back into the block under the case label.

However, the problem did *NOT* recur!

I suspect now that it was something like this:

int Func(int x)
{
switch (x)
{
case 8:
{
int DumVar = 72;
char Blat[51] = {'\0'};
sprintf(Blat, "DumVar = %d\n", Dumvar); // ERROR
MessagePopup("!!!", Blat); // prints "DumVar = 0"
}
}
}

See the problem? It's on the line marked "ERROR". It's
almost invisible. Hint: has to do with case.

:)

I'm about 80% certain now that something like that was the
problem. I can never be totally sure what happened, though.

Next time some bizarre bug like this happens, I'll
be sure to save a zip snapshot of source (my poor-man's
version of version control) before making any changes.
That way, I can post a stripped-down, compilable program
that actually does exhibit the bug.

Anyway, thanks for your help.
 
K

Keith Thompson

Robbie Hatley said:
I suspect now that it was something like this:

int Func(int x)
{
switch (x)
{
case 8:
{
int DumVar = 72;
char Blat[51] = {'\0'};
sprintf(Blat, "DumVar = %d\n", Dumvar); // ERROR
MessagePopup("!!!", Blat); // prints "DumVar = 0"
}
}
}

See the problem? It's on the line marked "ERROR". It's
almost invisible. Hint: has to do with case.

Got it. So you had a local variable named 'DumVar', and a variable in
an outer scope named 'Dumvar'? I assume you didn't use those names in
your actual code, but you might find that a more consistent naming
convention will help you avoid such problems in the future.

[...]
Next time some bizarre bug like this happens, I'll
be sure to save a zip snapshot of source (my poor-man's
version of version control) before making any changes.
That way, I can post a stripped-down, compilable program
that actually does exhibit the bug.

<OT>
Consider using an actual version control system. There are several
free ones out there, and they more than pay for themselves. (I use
RCS and CVS myself.)
</OT>
 
A

Army1987

I suspect now that it was something like this:

int Func(int x)
{
switch (x)
{
case 8:
{
int DumVar = 72;
char Blat[51] = {'\0'};
sprintf(Blat, "DumVar = %d\n", Dumvar); // ERROR
MessagePopup("!!!", Blat); // prints "DumVar = 0"
}
}
}

See the problem? It's on the line marked "ERROR". It's
almost invisible. Hint: has to do with case.
I wouldn't expect it to compile at all, unless there is a Dumvar
with a small v declared in an outer scope.
(And do they claim that CamelCase is better than dum_var because
you could unintentionally spell the latter with a wrong number of
underscores? Hum...)
 
D

Don Bruder

"Robbie Hatley said:
Keith Thompson said:
As for the problem you're having in your code, no offense,

None taken. :)
but I don't believe that it's as you describe it.

I didn't save a copy of the version that had the problem,
but I did manually reconstruct it by moving the variable
declarations back into the block under the case label.

However, the problem did *NOT* recur!

I suspect now that it was something like this:

int Func(int x)
{
switch (x)
{
case 8:
{
int DumVar = 72;
char Blat[51] = {'\0'};
sprintf(Blat, "DumVar = %d\n", Dumvar); // ERROR
MessagePopup("!!!", Blat); // prints "DumVar = 0"
}
}
}

See the problem? It's on the line marked "ERROR". It's
almost invisible. Hint: has to do with case.

If that's the actual problem, you would have (Or at least SHOULD have)
gotten an "undefined identifier: Dumvar" type of error from the
compiler, since, syntactically, that's exactly what you've got going on.
 
D

David Mathog

Robbie said:
I didn't save a copy of the version that had the problem,
but I did manually reconstruct it by moving the variable
declarations back into the block under the case label.

However, the problem did *NOT* recur!

Use valgrind or another memory access checker on problems
like this. One reason DumVar could have the wrong value
is an invalid memory access stomping on it. Putting in print
statements sometimes moves code around and "fixes" that sort
of problem, but not really of course.
I suspect now that it was something like this:

Didn't you keep the broken version of the program???
sprintf(Blat, "DumVar = %d\n", Dumvar); // ERROR

That should have thrown a compiler error, unless Dumvar was
declared elsewhere. Using both DumVar and Dumvar in the same function
would have been, well, "Dum"!

Regards,

David Mathog
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top