PDA

View Full Version : Why doesn't this switch statement work...




Aquis
Apr 3, 2009, 06:16 AM
I have a fairly simple switch statement which gives a syntax error if I try to create an object in it:

int number = 0;
switch (number) {
case 0:
NSObject * object = [[NSObject alloc] init];
break;
default:
break;
}

Yes, I realise that NSObject's don't do anything and yes, there's no point because I know for sure what number is at that point—it's just for an example. But I get a syntax error (error before * token) on the line where I create the object, and no matter what Objective-C object it is, it always creates an error. Why is this? Thanks!

Oh, and it's definitely aware of NSObject, I can just put in the same line without the switch statement no problem.



iSee
Apr 3, 2009, 06:48 AM
The problem is that the code declares a variable in a case block.
C doesn't like that. I suppose it's because of the ambiguities that arise. For example, what happens with object if the switch statement jumps to the default block.

You can resolve this by putting the case statements in a statement block:

int number = 0;
switch (number) {
case 0:
{
NSObject * object = [[NSObject alloc] init];
}
break;
default:
break;
}

gnasher729
Apr 3, 2009, 06:59 AM
I have a fairly simple switch statement which gives a syntax error if I try to create an object in it:

int number = 0;
switch (number) {
case 0:
NSObject * object = [[NSObject alloc] init];
break;
default:
break;
}

Yes, I realise that NSObject's don't do anything and yes, there's no point because I know for sure what number is at that point—it's just for an example. But I get a syntax error (error before * token) on the line where I create the object, and no matter what Objective-C object it is, it always creates an error. Why is this? Thanks!

Oh, and it's definitely aware of NSObject, I can just put in the same line without the switch statement no problem.

Buy a good C book. Better yet, download a copy of the ISO C Standard document; you can get a free copy of the latest draft by typing "N1124" into Google. Once you have done that, have a look at the syntax for labeled statements. That should make things very clear. To demonstrate what you learned, you might post _why_ "case 0: ;" works and why "case 0:" doesn't work.

mdeh
Apr 3, 2009, 07:31 AM
number[/FONT] is at that point—it's just for an example. But I get a syntax error (error before * token) on the line where I create the object, and no matter what Objective-C object it is, it always creates an error. Why is this? Thanks!


The expression following the keyword case must be an integral constant expression

ARM ( Harbison and Steele, V ed)

iSee
Apr 3, 2009, 08:09 AM
The expression following the keyword case must be an integral constant expression
ARM ( Harbison and Steele, V ed)


0 is the integral constant expression here, so that's not the problem.

mdeh
Apr 3, 2009, 09:21 AM
0 is the integral constant expression here, so that's not the problem.

Stand corrected! Your solution does the trick

gnasher729
Apr 3, 2009, 12:54 PM
Stand corrected! Your solution does the trick

But do you know why? Why does "case 0:;" instead of "case 0:" work?

mdeh
Apr 3, 2009, 02:02 PM
But do you know why? Why does "case 0:;" instead of "case 0:" work?

Well...afraid I am not sure what you are after.

Removing the statement works, placing it within it's own block works, doing what you suggests works. I assumed iSee's answer made sense. Not sure, but curious as to what you are getting at.

gnasher729
Apr 3, 2009, 03:28 PM
Well...afraid I am not sure what you are after.

Removing the statement works, placing it within it's own block works, doing what you suggests works. I assumed iSee's answer made sense. Not sure, but curious as to what you are getting at.

A label must be followed by a statement. The original poster followed the label by a declaration, which is not a statement. { int i = 0; } is a statement (compound statement). ";" on its own is an empty statement, the simplest of all statements. "int i = 0; " or something similar is not a statement, but a declaration.

iSee
Apr 3, 2009, 03:29 PM
I, too, don't understand what the "case 0:;" stuff is getting at.
I don't think this should work either :confused:

int number = 0;
switch (number) {
case 0:;
NSObject * object = [[NSObject alloc] init];
break;
default:
break;
}


I will probably give it a try, though, later and see what happens.

I thought that the C spec doesn't allow declaring variables where there is a mismatch between variable scope and flow control -- that is, it wants to prevent the situation where a program could jump over the declaration of a variable to a place where that variable is in scope. switch statements and gotos have this potential mismatch. while, do while and if statements do not have this potential. I'd be surprised if there was some way to trick out the compiler into allowing this after all. Live and learn, though!

mdeh
Apr 3, 2009, 05:12 PM
A label must be followed by a statement. The original poster followed the label by a declaration, which is not a statement. { int i = 0; } is a statement (compound statement). ";" on its own is an empty statement, the simplest of all statements. "int i = 0; " or something similar is not a statement, but a declaration.

Hi gnasher...yes, I see what you are saying. Just a small question. I thought that a statement like "int i = 0" is a definition and a declaration, but primarily a definition, in that the variable "i" has memory set aside for it, which is then assigned a value, in this case "0". Not sure if that changes anything you have said, though.

autorelease
Apr 3, 2009, 05:21 PM
I, too, don't understand what the "case 0:;" stuff is getting at.
I don't think this should work either :confused:

int number = 0;
switch (number) {
case 0:;
NSObject * object = [[NSObject alloc] init];
break;
default:
break;
}


I will probably give it a try, though, later and see what happens.

I thought that the C spec doesn't allow declaring variables where there is a mismatch between variable scope and flow control -- that is, it wants to prevent the situation where a program could jump over the declaration of a variable to a place where that variable is in scope. switch statements and gotos have this potential mismatch. while, do while and if statements do not have this potential. I'd be surprised if there was some way to trick out the compiler into allowing this after all. Live and learn, though!

It works because:
1) a label must be immediately followed by a statement; declarations are not statements (see page 131 of the ISO spec)
2) a single ; is an acceptable statement, it just does nothing

I'm not 100% sure of the rationale behind not allowing a declaration to follow a label, but it might be something like this: A label assigns a name to the address in memory of an instruction. A declaration doesn't generate instructions in the typical manner (it tells the compiler how much stack space to allocate) so it's unclear what memory address you would associate with the label.

It doesn't have anything to do with scope. Variables can be declared anywhere else in a switch statement. For example, the following code compiles with no errors:
switch (a)
{
int b = 9;
case 0:
printf("zero; b = %d\n", b);
int c = 10;
break;
case 1:
printf("one; b = %d, c = %d\n", b, c);
int d = 11;
default:
printf("other; b = %d, c = %d, d = %d\n", b, c, d);
}

b, c, and d are all in scope; space for them has been allocated on the stack. However, b's value will be garbage because the switch statement jumps over its initializer. Likewise, if a = 1, c will be used uninitialized, and if a is neither 0 nor 1, then b, c, and d will be used uninitialized.

The only reason that something like switch (a)
{
case 0:
int b = 9;
// ...
} doesn't work is because a label can only precede a statement, and declarations (even with initializers) are not statements. Likewise, this doesn't work either:

void myfunction()
{
if (!do_stuff())
goto mylabel;

mylabel:
int i = 3;
do_more_stuff();
}


The best way to declare variables in a case block is just to enclose the block in curly braces. While it's a nice trick, using lone semicolons leads to confusion (especially when used to indicate for/while loops with empty bodies)

iSee
Apr 3, 2009, 06:30 PM
It works because:
1) a label must be immediately followed by a statement; declarations are not statements (see page 131 of the ISO spec)
2) a single ; is an acceptable statement, it just does nothing


Yes, I see. It wasn't really the rule that a label must be followed by a statement rule that confused me. What I didn't understand is that it is legal to jump over a declaration. I looked in to this and found out where I went wrong. The actual rule, which I incorrectly remembered, is:

In C++, you can't jump over an initializer (i.e. a declaration where the variable is initialized with a value).

So I was wrong in 2 ways:

(1) I thought it was jumping over declarations, not initializers that was illegal.

(2) I thought this applied to C as well as C++. However in C it is perfectly OK to jump over both declarations and initializers (well, actually there are restrictions on jumping over the declarations of variable-length arrays and types based on variable-length arrays).

This all makes sense when I think about. In C++ it is very important that initializers be called -- that's part of the whole RAII thing. Skipping an initializer would mean you could have an object in scope that did not have a constructor called on it. So C++ has to be picker than C in order to support the RAII pattern.

lazydog
Apr 3, 2009, 06:41 PM
Hi

A trivial point but the Objective-C++ compiler does a much better job at compiling and picking up uninitialized variables in switch/case statements.
For example:-


switch ( i )
{
case 0 : int j = 1 ;
break ;

case 1 : break ;
}


will generate a cross initialisation error, but


switch ( i )
{
case 1 : break ;

case 0 : int j = 1 ;
break ;
}


compiles fine.

b e n

autorelease
Apr 3, 2009, 09:37 PM
(2) I thought this applied to C as well as C++. However in C it is perfectly OK to jump over both declarations and initializers (well, actually there are restrictions on jumping over the declarations of variable-length arrays and types based on variable-length arrays).

This all makes sense when I think about. In C++ it is very important that initializers be called -- that's part of the whole RAII thing. Skipping an initializer would mean you could have an object in scope that did not have a constructor called on it. So C++ has to be picker than C in order to support the RAII pattern.

Exactly. The little differences between C++ and C still trip me up quite frequently. :)