【C/C++】error: jump to case label

problem

The following is not allowed:


switch (a)
{
    case 1:
        int a = 6;
        //stuff
        break;

    case 2:
        //stuff
        break;
}


The following is allowed:


switch (a)
{
    case 1:
        {
            int a = 6;
            //stuff
        }
        break;

    case 2:
        //stuff
        break;
}

來自實際項目的一段代碼,簡化形式如下:
switch (t)
{
case 0:
int a = 0;
break;
default:
break;
}
有什麼問題嗎?似乎沒有。請用編譯器編譯一下……
嗯?!一個錯誤“error C2361: initialization of 'a' is skipped by 'default' label”。這怎麼可能?
幾番思琢,悟出解釋:C++約定,在塊語句中,對象的作用域從對象的聲明語句開始直到塊語句的結束,也就是說default標號後的語句是可以使用對象a的。如果程序執行時從switch處跳到default處,就會導致對象a沒有被正確地初始化。確保對象的初始化可是C++的重要設計哲學,所以編譯器會很嚴格地檢查這種違例情況,像上述的示例代碼中default語句後面並沒有使用a,但考慮到以後代碼的改動可能無意中使用,所以一樣被封殺。
明白了原因,解決起來就很容易了。只要明確地限制對象a的作用域就行了。
switch (t)
{
case 0:
{ //added for fix problem
int a = 0;
break;
} //added for fix problem
default:
break;
}
如果確實需要在整個switch語句中使用對象a,那就把int a = 0;移到switch語句之前即可。不過從原先的語句看,其意圖似乎並不是這樣的,所以推薦前面的解決方案。


結束了嗎?沒有。讓我們繼續考究錯誤提示信息中“initialization”(也就是初始化)的確切含義。C++很看重初始化,所以往往會給我們造成一種錯覺,似乎對象在定義處一定會經過初始化過程。真實情況如何呢?還是用實例來證明吧。
switch (t)
{
case 0:
int a;
a = 0;
break;
default:
break;
}
編譯,這次沒有報錯。很明顯int a;定義了對象,但沒有進行初始化,否則就應該報告原先的錯誤。
再看看用戶自定義類型。
class B
{
};

switch (t)
{
case 0:
B b;
break;
default:
break;
}
編譯結果也沒有錯誤,所以沒有提供構造器的類仍然沒有初始化過程。
如果給類加入構造器,情況就不同了。
class B
{
public: //added for initialization
B(){} //added for initialization
};
這樣就能重現原先的錯誤。證明有了構造器,編譯器就將進行初始化處理並對之進行安全檢查。


從上面的實驗,可以直觀地體驗到一些基本的C++觀念和原理,並提高認識深度。
1.int a = 0;既是聲明也是定義,還包括初始化;int a;是聲明還是定義依上下文而定,但如果是定義就不會包括初始化;a = 0;僅僅是賦值語句,在此句前對象已經存在了。
2.爲了避免不必要的開銷,默認情況下,即程序員沒有在代碼中明確指示時,編譯器不提供初始化過程。某些需要確保初始化的類,請提供構造器。這裏透露出一個C++的設計哲學:通常你會面對多種選擇,所以請精確地控制代碼,其收益則是可以自由取捨調配的安全性、速度、內存開銷等程序特性。
3.嚴密注意程序中標號的使用情況,特別是case、default等常規標號,否則他們可能會破壞對象的正確狀態。如果提供了對象初始化,則能夠獲得編譯器的額外幫助。

問題:
1>
"
如果程序執行時從switch處跳到default處,就會導致對象a沒有被正確地初始化。確保對象的初始化可是C++的重要設計哲學,所以編譯器會很嚴格地檢查這種違例情況,像上述的示例代碼中default語句後面並沒有使用a,但考慮到以後代碼的改動可能無意中使用,所以一樣被封殺。
"
它還會考慮到以後代碼的改動可能無意中使用這種情況嗎?

2>
class B
{
};
爲什麼聲明一個B對象時作者說沒有初始化,類不是在沒有聲明構造函數時會用它的默認構造函數來初始化對象嗎?爲什麼作者說沒有初始化呢?

3>
爲什麼說沒有初始化就會出問題呢?在C++中所有對象一定要初始化?不是這樣的吧.
作者好像就這個意思啊.!

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

首先要承認作者提出這個問題很好,很多人都會碰上的這種情況,但是他本身對這個問題的理解個人認爲存在誤區或者說不到位。
下面是我的分析:

代碼:
switch (t)
{
case 0:
    int a = 0;
    break;
default:
    break;
}g++ 下的錯誤提示是:
case_init.cpp:9: error: jump to case label
case_init.cpp:7: error: crosses initialization of `int a'
首先在這裏作者提到問題出在本地(local)變量或者叫做自動變量的作用域範圍,點的很好。本地變量的作用域僅在花括號之間。於是方案1:

代碼:
 switch (t)
{
case 0:
  { //added for fix problem
     int a = 0;
     break;
  } //added for fix problem
default:
     break;
}問題得到解決,在新的約束範圍內,變量 a 很好的工作,而且也正確的實現了意圖。接着方案2:

代碼:
switch (t)
{
case 0:
    int a;
    a = 0;
    break;
default:
    break;
}也沒錯。似乎兩個方案都完成了目的,而且等價。於是作者把注意力完全轉移到初始化問題上,糾住不放,到最後也沒講清楚爲何有時候可以初始化,有時候確不能。
其實再深入一步,就完全清楚了,我把程序改動了一下,加了幾個打印語句:

代碼:
#include <stdio.h>
int main()
{
        printf("請輸入一個數字: ");
        int t;
        scanf("%d", &t);
        switch (t)
        {
                case 0:
                        int a;
                        printf("/n a = %d/n", a = 0);
                        break;
                case 1:
                        printf("/n a = %d/n", a = 1);
                        break;
                default:
                        printf("/n a = %d/n", a = t);
                        break;
        }
}編譯通過,然後運行一下看看,一切正常。可能有人會很奇怪, int a 是在 case 0 中聲明的呀!如果運行時選擇 1,不就跳過去了嗎?怎麼在 case 1 和 default 中也能用呀?
殊不知不管你是有沒有選 0, 這樣定義的局部變量 a 在它的作用域(從聲明處到結束花括號)範圍之內
都有效。
到這裏該明白編譯器爲何對 int a = 0; 初始化作出反應吧!
雖然你自己知道只在 case 0 中用到 a,在 case 1 或 default 中都不會用到 a(非上例),但編譯器不懂你的心思,
爲了避免在其它地方用到該變量出現不必要的麻煩,做一個出錯提醒。 至於下面這個 class 的例子,道理一樣。
所謂盡信書不如無書,何況並非大師的手筆,我們看的時候得多長個心眼,多動動手有助於問題分析。

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/eroswang/archive/2009/03/03/3954036.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章