关于在“switch”语句中“case”语句中的大括号

10
今天,当我试图编写代码来简单地添加和减去两个2*2矩阵时,我使用了一个switch语句,结果出现了错误:

在main()函数中,case绕过了局部变量的初始化。

代码

#include <iostream.h>
#include <conio.h>
#include <string.h>

int
main()
{
    int mat1[2][2], mat2[2][2], mat3[2][2];

    cout << "Enter the elements in the first matrix";
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            cin >> mat1[i][j];
        }
    }

    cout << "\n\nEnter the elements of the second matrix";

    for (int k = 0; k < 2; k++) {
        for (int l = 0; l < 2; l++) {
            cin >> mat2[k][l];
        }
    }

    cout << "\n\nsaved......";

    int choice;
    cout << "\n\n\nFor adding these two matrices,press 1";
    cout << "\nFor subtracting these two matrices,press 2";
    cin >> choice;

    switch (choice) {
    case 1:

        cout << "The addition of the two matrices will yield";
        for (int a = 0; a <= 1; a++) {
            for (int b = 0; b <= 1; b++) {
                mat3[a][b] = mat1[a][b] + mat2[a][b];
            }
        }
        break;

    case 2:
        cout << "The subtraction of the two matrices will yield";
        for (int c = 0; c <= 1; c++) {
            for (int d = 0; d <= 1; d++) {
                mat3[c][d] = mat1[c][d] - mat2[c][d];
            }
        }
        break;
    }
    getch();
    return 0;
}

我发现将case(s)的代码放入大括号中可以消除这个错误,但是我对错误本身和case语句中大括号的要求感到困惑。(我知道我没有使用新的编码规范,如,std命名空间等等,因为我是在Turbo C ++编译器中编写的,所以恳请给出简明扼要的答案。)

哪一行代码导致了这个错误? - matzahboy
1
这不是一个答案,但是case语句的两个分支都会将两个矩阵相加,之后没有任何输出。我猜这不是你想要的。 - zwol
粗体文本并没有告诉我们它所指代的行号,可以参考我的答案。 - Keith Thompson
3个回答

25

一个switch语句只是一堆标签和一个goto,取决于switch测试内部的值,由编译器执行。

当你在函数中有一个局部变量时,你可以在该变量声明之后的任何地方使用它。例如:

int a;
// can use a now

然而,在 switch 语句中,如果你有一个局部变量:

case a:
    int a;
    break;
case b:
    // we can use a here because these cases are just labels used in a goto
    // i.e. the cases do *not* create a new scope

当你在一个case中有一个变量时,该变量存在于其下方的case中,但由于被case语句跳过了初始化该变量的代码,所以该变量不会存在。这很难解释,也许其他人可以做得更好。

使用大括号可以解决这个问题,因为它们使变量成为局部变量,这样它就不会存在于后续的case中。只有在特定的case被执行时才会创建它,如果你忘记了break,控制权将流到下一个case,结束的}会结束作用域并导致变量被销毁,因此它不能从下一个case中访问,初始化也无法被跳过。

因此,请记住所有的case共享作用域。这可能有助于您理解这个问题。


我在 OP 发布的代码中没有看到类似的情况。实际上,它可以在 gcc 上顺利编译,http://www.ideone.com/tgpuT。 - Alok Save
@Als 是的,我知道,因为for循环中变量的作用域是受控制的,但我不知道他怎么会在没有那种情况下得到那个错误消息,所以我假设代码不是错误的来源,或者它并不是真实存在的。 - Seth Carnegie
1
因为他正在使用一个糟糕的老编译器,该编译器不符合标准。 - Alok Save
@Als:公平地说,它可能符合当时存在的事实标准。我认为Stroustrup在《C++的设计与演化》中提到了这个问题。 - Keith Thompson

7

在重新缩进您的代码并更改一些内容以使其在我的系统上编译后,g++没有警告地编译它。

我最好的猜测是与for循环中声明对象作用域的旧规则有关(它过去存在于封闭作用域的末尾;在现代C ++中,它仅限于循环本身),但我无法确定。

为了帮助我们解决这个问题,请正确缩进代码,并显示精确的错误消息,包括行号。如果错误显示“第42行:...”,请在源代码中添加注释 // 这是第42行

编辑:是的,我认为这就是问题所在。在现代C ++中,您的代码很好,因为循环变量作用域仅限于循环本身。显然,Turbo实现了语言的一个非常旧的版本,因此例如您的变量a 可以一直可见到 switch 语句底部。将每个 for 循环用花括号括起来应该可以避免出现警告:

{
    for (int a = 0; a <= 1; a++) {
        for (int b = 0; b <= 1; b++) {
            mat3[a][b] = mat1[a][b] + mat2[a][b];
        }
    }
}

EDIT2:或者更好的选择是停止使用Turbo C++,换用现代编译器。
EDIT3:编译器警告的原因是,虽然看起来i总是在使用之前被初始化,但在case 2:部分可以绕过初始化而引用i。(再次说明,这仅适用于旧规则。)
一种更干净的解决方法可能是在每个case部分中加上大括号:
switch (choice) {
    case 1: {
        ...
        break;
    }
    case 2: {
        ...
        break;
    }
}

(或者,再次强调,除非您有非常好的理由坚持使用Turbo C++,否则请获取现代编译器。)

尊敬的先生,实际上在Turbo C++中没有这样做的方法。错误消息与我在问题中粗体显示的相同。 - nobalG
真的吗?它只是给你一个错误消息,而没有告诉你源代码中哪一行出了问题?还是它会把你带到一个编辑器中,并将光标放在有问题的那一行上?如果是这样,那么是哪一行呢? - Keith Thompson
它仅突出显示文本“case 1:” - nobalG
这个问题有一个古老的宏技巧。严格来说是非法的,但它确实有效 #define for if (0) ; else for。将其放在代码开头,每个for循环都会被放入else块中,因此适当的作用域规则就会应用。曾经为我节省了数天的工作时间,自那以后我就一直记得它。 - john
Turbo C++(来自1990年代)是许多印度工程学院所必需的(或者是学生强制性实习场所所必需的)。正如Joshua Fluke的几个视频所记录的那样,即使在2020年,这种情况仍然存在(我找不到确切的参考资料,但是大约是在去年左右)。 - Peter Mortensen
显示剩余2条评论

5

case块本身并不是一个新的作用域。你在case块中声明的任何变量都可以在整个switch语句中可见。但在其他的case块中,它是未初始化的。通过添加花括号,您可以创建一个新的作用域,因此其他块无法看到它。

例如:

switch (choice)
{
case 0:
    int a = 42;
    break;


case 1:
    std::cout << a << std::endl; // 'a' is uninitialized here
    break;
}


switch (choice)
{
case 0:
    {
        int a = 42;
        break;
    }


case 1:
    std::cout << a << std::endl; // error -- 'a' is not declared here
    break;
}

我在你发布的代码中没有看到类似这样的情况,但这就是错误信息的含义。


你可能想要链接到这个网址:[http://www.ideone.com/tgpuT],gcc-4.3.4没有显示任何错误。 - Alok Save
@Als: 你试过加上-Wall参数吗? - Fred Larson
@Als:你的意思是这里。那就是楼主的代码?正如我所说,我在那段代码中没有看到这种情况。我认为我们没有看到有问题的代码或者修复它的大括号。 - Fred Larson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接