4

I've read this question about the "jump to case label" error, but I still have some questions. I'm using g++ 4.7 on Ubuntu 12.04.

This code gives an error:

int main() {
  int foo = 1;
  switch(foo) {
  case 1:
    int i = 0;
    i++;
    break;
  case 2:
    i++;
    break;
  }
}

The error is

jump-to-case-label.cpp: In function ‘int main()’:
jump-to-case-label.cpp:8:8: error: jump to case label [-fpermissive]
jump-to-case-label.cpp:5:9: error:   crosses initialization of ‘int i’

However, this code compiles fine,

int main() {
  int foo = 1;
  switch(foo) {
  case 1:
    int i;
    i = 0;
    i++;
    break;
  case 2:
    i++;
    break;
  }
}

Is the second code any less dangerous than the first? I'm confused as to why g++ allows it.

Secondly, the fix for this problem is to scope the initialized variable. If the initialized variable is a large object, and the switch statement is in a while loop, won't the constructor and destructor be called each time that scope is entered and left, causing a decrease in efficiency? Or will the compiler optimize this away?

Community
  • 1
  • 1
gsgx
  • 12,020
  • 25
  • 98
  • 149
  • I don't get why that compiles. If you go straight to case 2 your `i` hasn't even been declared! – alestanis Oct 20 '12 at 19:58
  • (The "secondary" error message seems more relevant to the question.) –  Oct 20 '12 at 20:00
  • 2
    Could the downvoter please explain their downvote? – gsgx Oct 20 '12 at 20:07
  • @alestanis: Why do you think `i` hasn't been declared? It is clearly defined in the block on the line after the `case 1:` label. The `break` statements **don't** magically split the block into different scopes. It wouldn't make any sense to do so because they can be conditionally executed if the programmer chooses to do so. – Dietmar Kühl Oct 20 '12 at 20:09

4 Answers4

6

Jumping past the initialization of an object, even if the object is of type int, is always undefined behavior. Note, that the switch-statement's statement isn't anything special: It is just a statement and people have [ab-]used this interesting ways, e.g., for Duff's Device. The only thing which is special within the statement is that labels can take the form default: and case <const-integer-expr>:.

The statement int i; is a definition of the variable but no initialization. Thus, no initialization of a variable is by-passed. There is no bigger problem jumping past this definition than there is in the first place. Of course, the value gets assigned when jumping to case 1: and not when jumping to case 2: but this is no different than what happens in code outside of switch-statements if people only define variables.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • @alestanis: Of course, it compiles: the code is well-formed and just results in undefined behavior when jumping to the `case 2:` label. It should spit out a warning, though, if `foo == 2`. Just spotted why it doesn't warn: it doesn't warn because the variable is just defined but not initialized. An initialization would look like `int i = 0;`. – Dietmar Kühl Oct 20 '12 at 20:13
2

At first, compiler decides how it will open switch to assembly code. May be if or may be table as goto. Also, if you write variable initialization together declaration, compiler says you that this is error. In another cases it will be warning (compiler warns you, but does not resist you). So, you can protect yourself. Just it tunes compiler options, where warnings it will be errors. And to work correctly with variables in the switch, you must specify their scope. For example:

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

PS. For c++, switch is hell.

From C++11:

C.1.5 Clause 6: statements

6.4.2, 6.6.4 (switch and goto statements)

Change: It is now invalid to jump past a declaration with explicit or implicit initializer (except across entire block not entered)

Rationale: Constructors used in initializers may allocate resources which need to be de-allocated upon leaving the block. Allowing jump past initializers would require complicated run-time determination of allocation. Furthermore, any use of the uninitialized object could be a disaster. With this simple compile-time rule, C++ assures that if an initialized variable is in scope, then it has assuredly been initialized.

Effect on original feature: Deletion of semantically well-defined feature.

From gcc diagnostic pragmas:

6.57.10 Diagnostic Pragmas Diagnostic Pragmas

GCC allows the user to selectively enable or disable certain types of diagnostics, and change the kind of the diagnostic. For example, a project's policy might require that all sources compile with -Werror but certain files might have exceptions allowing specific types of warnings. Or, a project might selectively enable diagnostics and treat them as errors depending on which preprocessor macros are defined.

PSS. Compiler knows that into the chunk of code your variable uninitialized. Whatever it been static C/C++ analysis (For example, for free cppcheck) show you problem place.

Ruu
  • 1,245
  • 9
  • 9
  • Of course, what do you think of Martin Fowler and polymorphism? – Ruu Oct 20 '12 at 20:57
  • I think splitting logic across 20 classes each with a private method to avoid one switch statement will keep me employed a long time fixing their code after they're fired. – stark Oct 20 '12 at 21:35
  • 1
    I talked about the abuse switches'. – Ruu Oct 20 '12 at 23:10
1

Turning on warnings (-Wall) in the second case gives:

foo.cpp:15:8: warning: 'i' may be used uninitialized in this function [-Wuninitialized]

Jumping past initialization is an error, but the compiler doesn't try to outguess you on uninitialized variables.

stark
  • 12,615
  • 3
  • 33
  • 50
0

Initializing a variable inside switch causes this error. This happens due to a permission issue. Try initializing variable outside the switch and assign values inside switch.

Same issue applies to C++ threads.

Choxmi
  • 1,584
  • 4
  • 29
  • 47