Why doesn't this switch statement work...

Discussion in 'Mac Programming' started by Aquis, Apr 3, 2009.

  1. macrumors member

    Joined:
    Aug 7, 2007
    Messages:
    63
    Location:
    Staffordshire, UK
    #1
    I have a fairly simple switch statement which gives a syntax error if I try to create an object in it:
    Code:
    	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.
     
  2. macrumors 68040

    iSee

    Joined:
    Oct 25, 2004
    Messages:
    3,325
    #2
    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:
    Code:
    	int number = 0;
    	switch (number) {
    		case 0:
    			{
    				NSObject * object = [[NSObject alloc] init];
    			}
    			break;
    		default:
    			break;
    	}
    
     
  3. macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    Messages:
    14,249
    #3
    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.
     
  4. macrumors 6502

    Joined:
    Jan 3, 2009
    Messages:
    344
    #4
    [QUOTE
    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!
    [/QUOTE]

    ARM ( Harbison and Steele, V ed)
     
  5. macrumors 68040

    iSee

    Joined:
    Oct 25, 2004
    Messages:
    3,325
    #5
    0 is the integral constant expression here, so that's not the problem.
     
  6. macrumors 6502

    Joined:
    Jan 3, 2009
    Messages:
    344
    #6
    Stand corrected! Your solution does the trick
     
  7. macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    Messages:
    14,249
    #7
    But do you know why? Why does "case 0:;" instead of "case 0:" work?
     
  8. macrumors 6502

    Joined:
    Jan 3, 2009
    Messages:
    344
    #8
    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.
     
  9. macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    Messages:
    14,249
    #9
    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.
     
  10. macrumors 68040

    iSee

    Joined:
    Oct 25, 2004
    Messages:
    3,325
    #10
    I, too, don't understand what the "case 0:;" stuff is getting at.
    I don't think this should work either :confused:
    Code:
    	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!
     
  11. macrumors 6502

    Joined:
    Jan 3, 2009
    Messages:
    344
    #11
    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.
     
  12. macrumors regular

    Joined:
    Oct 13, 2008
    Messages:
    144
    Location:
    Achewood, CA
    #12
    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:
    Code:
    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
    Code:
    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:
    Code:
    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)
     
  13. macrumors 68040

    iSee

    Joined:
    Oct 25, 2004
    Messages:
    3,325
    #13
    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.
     
  14. macrumors 6502a

    Joined:
    Sep 3, 2005
    Messages:
    686
    Location:
    Cramlington, UK
    #14
    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:-

    Code:
    switch ( i )
    {
       case 0 :	int j = 1 ;
    		break ;
    			
       case 1 :	break ;
    }
    
    will generate a cross initialisation error, but

    Code:
    switch ( i )
    {
       case 1 :	break ;
    
       case 0 :	int j = 1 ;
    		break ;			
    }
    
    compiles fine.

    b e n
     
  15. macrumors regular

    Joined:
    Oct 13, 2008
    Messages:
    144
    Location:
    Achewood, CA
    #15
    Exactly. The little differences between C++ and C still trip me up quite frequently. :)
     

Share This Page