• C++中使用switch..case语句的易出错陷阱和规避方法


    C++作为C语言的升级版,支持很多C语言不支持的语法。例如,函数中的局部变量不必在函数的最开始统一定义了,在函数内部随时定义新的局部变量成为可能。

    比如下面的示例代码,在for循环的初始条件中定义了用于计数的整形变量i,这是不符合C语言语法规定的,故而无法通过C语言编译器的编译。

    int fun()
    {
      int n = 6;
      
      for (int i = 0; i < 3; i++)
      {
        n += i;
      }
      
      return n;
    }
    

    必须把代码修改为如下所示,在函数的开头定义整形变量i,才能通过编译。

    int fun()
    {
      int n = 6;
      int i = 0;
    
      for (i = 0; i < 3; i++)
      {
        n += i;
      }
    
      return n;
    }
    

    不过,如果使用C++编译器,以上两段代码都是符合语法规定的,都可以通过编译。

    回到主题,这里要说一个C++在语法方便的同时带来的隐患。来看这一段C++的swtich..case代码:

    void fun(int nInput)
    { 
      switch(nInput)
      {
      case 1:
        int n;
        n = 1;
        printf("case1");
        break;
      case 2:
        printf("case2");
        break;
      default:
        printf("case defalut");
        break;
      }
    }
    
    int main(int argc, char* argv[])
    {
      fun(8);
      return 0;
    }
    

    这段代码利用了刚才提到的C++新的语法支持,在switch..case的分支case 1中,定义了整形变量n,并且把它赋值为1。这段代码完全合法,编译通过(VS2012环境中),运行结果如下图所示,一切正常。

    问题出现在当我们尝试初始化整形变量n的时候。定义变量的同时初始化,是一个好习惯,然而,此时对n的初始化却会引发错误导致无法编译。修改示例代码中的case 1部分,尝试把整形变量n初始化为0:

    void fun(int nInput)
    { 
      switch(nInput)
      {
      case 1:
        int n = 0;
        n = 1;
        printf("case1");
        break;
      case 2:
        printf("case2");
        break;
      default:
        printf("case defalut");
        break;
      }
    }
    
    int main(int argc, char* argv[])
    {
      fun(8);
      return 0;
    }
    

    编译报错如下图所示。大致意思是说n的初始化操作被跳过了。

    回想函数调用过程,在函数的参数、当前代码地址、栈地址入栈之后,紧接着系统会给函数内部的局部变量在栈里划分一片空间,这片划分出来的空间入栈之后,系统会给所有被初始化的局部变量赋予初始值。

    如此一来,在示例代码中的情况下,C++编译器就不知所措了。整形变量n的作用域是swtich..case结构被花括号括起来的整个部分:虽然整形变量n的定义在case 1标签下面,但它对于case 2和case default都是可见的,可以把case 2和case default理解为整形变量n的“利益相关者”。站在编译器的角度,如果对整形变量n进行初始化操作,那么则相当于默认switch..case会跳转到case 1标签下,这显然是一种置case 2和case default于不顾的非法行为;如果不进行初始化操作,那么编译器就没有完整翻译程序源代码,没有完成自己的职责。在这种两难境地下,编译器只好选择报错了。

    有没有一种解决方案,既能让我们充分利用C++灵活的语法规定(在switch..case结构内部也可以定义局部变量),又能够让我们保持定义局部变量后立即初始化的良好习惯,而且还不让编译器为难(报错)呢?

    答案是有的!解决思路是把定义的局部变量隔绝起来,达到缩小局部变量作用域的效果,也就是让其他case分支看不到它。正所谓“眼不见心不烦”,其他的分支看不到这个局部变量,也就不会产生什么意见了。具体的解决方案就是在每一个case的标签下面都嵌入一对花括号。

    修改后的代码如下所示:

    void fun(int nInput)
    { 
      switch(nInput)
      {
      case 1:
        {
        int n = 0;
        n = 1;
        printf("case1");
        break;
        }
      case 2:
        {
        printf("case2");
        break;
        }
      default:
        {
        printf("case defalut");
        break;
        }
      }
    }
    
    int main(int argc, char* argv[])
    {
      fun(8);
      return 0;
    }
    

    经测试,编译通过,如下图所示:

    当然,case 2和case defalut的下面不是必须要加一对花括号,因为它们下面并没有局部变量的定义和初始化操作。但是,在所有case label下都加上一对花括号是一个很好的习惯,因为随着代码量的增加,万一这个label下面发生了变量的定义和初始化操作而没有引起注意的话,调试起来可能会很麻烦。在那种情况下,编译器的提示信息可能不会像本文示例这样给出明确的错误描述,而是可能会因为上下文环境的原因而给出晦涩不清的错误描述,让人一下看不出问题到底出在了哪里。

    总结:在享受C++语法便利的同时,不要忘记了这些潜在的小陷阱,灵活利用花括号来限制局部变量的作用域。

  • 相关阅读:
    名字 地址 路由
    截断二进制指数退避
    硬件地址 软件地址
    基带信号 调制
    非对称数字用户线
    码分复用 码分多址
    时分复用 帧长度
    多模光纤 单模光纤
    码间串扰 奈氏准则 香农公式
    flask 文件下载 文件服务器 请求参数 函数修饰符
  • 原文地址:https://www.cnblogs.com/zhugehq/p/5947699.html
Copyright © 2020-2023  润新知