87

Consider the following code:

#include <iostream>
using namespace std;

int main()
{
    int x, y, i;
    cin >> x >> y >> i;
    switch(i) {
        case 1:
            // int r = x + y; -- OK
            int r = 1; // Failed to Compile
            cout << r;
            break;
        case 2:
            r = x - y;
            cout << r;
            break;
    };
}

G++ complains crosses initialization of 'int r'. My questions are:

  1. What is crosses initialization?
  2. Why do the first initializer x + y pass the compilation, but the latter failed?
  3. What are the problems of so-called crosses initialization?

I know I should use brackets to specify the scope of r, but I want to know why, for example why non-POD could not be defined in a multi-case switch statement.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jichao
  • 40,341
  • 47
  • 125
  • 198
  • 1
    My understanding, given the answers below, for point 3 is that this error is an excessive restriction of c++. If r is not used after the label, there is no impact (even if the example here uses r, it can be removed in case 2 and the compiler would give the same error). The better proof is that it is allowed in C, and even in C11. – calandoa May 27 '13 at 10:52
  • Possible duplicate of [Error: Jump to case label](http://stackoverflow.com/questions/5685471/error-jump-to-case-label) – Ciro Santilli OurBigBook.com Mar 02 '16 at 20:24

4 Answers4

103

The version with int r = x + y; won't compile either.

The problem is that it is possible for r to come to scope without its initializer being executed. The code would compile fine if you removed the initializer completely (i.e. the line would read int r;).

The best thing you can do is to limit the scope of the variable. That way you'll satisfy both the compiler and the reader.

switch(i)
{
case 1:
    {
        int r = 1;
        cout << r;
    }
    break;
case 2:
    {
        int r = x - y;
        cout << r;
    }
    break;
};

The Standard says (6.7/3):

It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps from a point where a local variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has POD type (3.9) and is declared without an initializer (8.5).

avakar
  • 32,009
  • 9
  • 68
  • 103
  • But my G++ does allow `int r = x + y`. – Jichao Mar 06 '10 at 14:04
  • 12
    Well, my g++ doesn't. Check again or upgrade the compiler. – avakar Mar 06 '10 at 14:07
  • thanks, that was helpful to me. I think that C compiler does not even allow declaration to come _after_ some code. Apparently C99 allows that though... http://stackoverflow.com/questions/7859424/why-was-mixing-declarations-and-code-forbidden-up-until-c99 – m-ric Aug 22 '12 at 16:06
37

You should put the contents of the case in brackets to give it scope, that way you can declare local variables inside it:

switch(i) {
    case 1:
        {
            // int r = x + y; -- OK
            int r = 1; // Failed to Compile
            cout << r;
        }
        break;
    case 2:
        ...
        break;
};
Péter Török
  • 114,404
  • 31
  • 268
  • 329
3

It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps from a point where a local variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has POD type and is declared without an initializer.

[Example: Code:

void f()
{
  // ...
  goto lx;    // ill-formed: jump into scope of `a'
  // ...
 ly:
    X a = 1;
  // ...
 lx:
   goto ly;    // ok, jump implies destructor
 // call for `a' followed by construction
 // again immediately following label ly
}

--end example]

The transfer from the condition of a switch statement to a case label is considered a jump in this respect.

Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
Ashish Yadav
  • 1,667
  • 6
  • 20
  • 26
  • 1
    Welcome to Stack Overflow. You should provide sources for your citations, this is C++03:6.7/3. It also happens to be the same paragraph I cited in my answer. – avakar Mar 06 '10 at 15:19
0

I suggest you promote your r variable before the switch statement. If you want to use a variable across the case blocks, (or the same variable name but different usages), define it before the switch statement:

#include <iostream>
using namespace std;

int main()
{
    int x, y, i;
    cin >> x >> y >> i;
// Define the variable before the switch.
    int r;
    switch(i) {
        case 1:
            r = x + y
            cout << r;
            break;
        case 2:
            r = x - y;
            cout << r;
            break;
    };
}

One of the benefits is that the compiler does not have to perform local allocation (a.k.a. pushing onto the stack) in each case block.

A drawback to this approach is when cases "fall" into other cases (i.e. without using break), as the variable will have a previous value.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
  • 2
    I would suggest doing it the other way around. Compiler does not have to perform "local allocation" in either case (and in practice it won't). – avakar Mar 06 '10 at 15:16