switch case、gotoの変数定義への影響

4384 ワード

dingmzfrocのブログhttp://blog.163.com/dingmz_fracmyblog/blog/static/21730223203732320739/
switch caseとgotoを使っている時はとても楽しかったです.プログラムはジャンプして、脳のない実行はもう大丈夫です.しかし、その原理が分からないと、間違いやすいです.しかも変な間違いですよ.
説明:この文章の分類はC++であるため、ここで述べた規則はC++にしか適用されません.C言語には異なるルールがあります.
まず下のコードを見てみます.問題がありますか?
void RunStatiteMachine(){switch(mustatus){case TASKUSTART:int data=10;break;case TASK:/.break;//.}
初めて見ても問題はないと思いますが、コンパイラが間違っています.
initialization of'data's skyped by'case'label.
Why and what
これはcaseで変数を定義する問題点であり、switch-caseの「通り抜け」とも言われています.
C++標準を詳しく調べたら、C++の中で、switch-caseのcaseは実質的に一つのタグで、gotoのラベルのようです.caseのコードは局所的な作用領域を構成していません.そのインデントは人に錯覚を与えますが、それは作用領域のようです.つまり、caseで定義されている変数のスコープはすべてswitch{...}であり、後の他のcaseでもこの変数にアクセスできます.switchは本質的にgotoに相当するので、switchに続く印刷文は永遠に実行されません.
switch(selector){cout<
これは間違いと何の関係がありますか?C++標準規定: 
It ispossiblble to trnsfer into a block、but not in a way that bypasses declaratis with initialization.A programthat jums from a pointwhehere a locacal variaaaaaaaaaatotonistoragagage duration isisisisisisststorororororororage duraration isisisisisisisisisisisisisisisisisisstststststststststororororororororororororororororororororororororororororororororororororororororororororororororororororororzer.(The transfer from the condition of a switch statement to a case label is consided a jump in this repect.)
という意味です
プログラムの実行パスがコードの中のポイントA(まだ定義されていない部分変数x)からコードのもう一つのポイントB(この部分変数xが定義されていて、定義されているときに初期化されています.)にジャンプすると、コンパイラはエラーを起こすことができます.このようなジャンプは、goto語文またはswitch-caseの実行に起因することができます.
ここには二つの状況があります.
1、PODオブジェクトに対しては、初期化された宣言がスキップされた場合のみエラーが発生します.
switch(selector){case selectorual=1;/もし「int i;」であれば、コンパイラはエラーを報告しません.次の警告case selectorub:なお、変数iが定義されているかどうかは、コンパイル期間で決定されますが、初期化は実行時の挙動です.スキップは初期化であり、定義ではありません. 
ここに来て、私達はすべて原因を知っています.caseとはラベルです.役割はgotoのラベルと同じです.だから、switchの中の一つのcaseで定義されている変数は実はすべてのcaseに対して見られて有効です.問題が発生します.上のコードのように、switchがジャンプしたら問題があります.
selectorualであれば、iはスムーズに定義され、空間を割り当てることができます(局所変数の初期化定義は正常な語句であり、実行後変数は空間を割り当てられ、値が与えられます).
selectorubはどうなりますか?iの初期化定義はそのままスキップされます.
!
ただし、switch caseで変数だけを定義しても、初期値を付与していない場合は、エラーは発生しません(
PODオブジェクト)
2、構造関数を明示的に定義したクラスオブジェクトに対して、その宣言はスキップされた時にエラーが発生します.
class Employee{public:Employee(){.}void f(.);.switch(selector){case selectoria:Employee]//コンパイルエラー!case selectorub:e.f();
PODオブジェクトが初期化されている場合、またはクラスが明示的な立体構造関数を持っている場合、プログラマはこのオブジェクトを初期化したいと考えています.
任意の可能性がありますが、初期化はスキップされ、プログラムはプログラマの予想外の何らかの不正な状態に入る可能性があります.
もちろん、初期化されていないPODオブジェクトに対しては、後の読み取りに問題があるかもしれません.大丈夫です.使用前に割り当てが行われたかどうかによって異なります.そして、これらはすべて実行時に知っています.コンパイラは何もできません.ですから、ほとんどの場合はエラーを報告しません.
[cpp]view plincopyperint?void f(boot set){goto label;int i;label:if(set){i=3}cout<はい、問題に戻ります.上記のコンパイルエラーが発生した根本的な原因は、対象の役割ドメインが「ドラッグ」が長いため、複数のcaseを超えています.解決策は、大きい括弧でcase毎のコードを一部の作用領域に封じます.iは局所的な役割領域を持っていますので、声明がスキップされる問題はありません. 
switch(selector){case selectoria:{int i=1;}case selectorub:/iはここでは見られません}
gotoの「通り抜け」
基準を見ていると、もう一つの面白い例が発見されました.この例は、前へ通る変数の定義に問題があるということを説明するために、これは上で議論されています.しかし、goto lyを実行するときは、オブジェクトaの構造関数を呼び出します.
void f(){/…goto lx;/コンパイルエラー、原因は分析///...ly:A=1、///…lx:goto ly、//OK、ジャンプはlyという意味でaのコンフィギュレーション関数はA b=aと呼ばれる;/OK、ここはaの役割領域に属しますが、永遠に実行されません.このプログラムはデッドサイクルを形成しています.最初にaの解析が呼び出されて、構造されて、分析されて、どんどん巡回しています.なぜgoto lyを実行する時にaのコンストラクターを呼び出すのですか?これは標準の中にも専門的な説明があります.
Transfer out of a loop、out of a block、or back past an initialized variable with atomatic storage duration involves the destruction of variables with auttic storge duration that are in scope at the point transferred from but not the point transferred to.
プログラム実行経路がループ、ローカルスコープから離れていたり、初期化されたオブジェクトの前に戻ってきたりすると、対応する構文関数が呼び出されます.
上記の例では、オブジェクトaの役割領域はA=1から始まり、fの右括弧まで終わっています.プログラム実行がA=1に達する前には、aの役割領域には明らかになくなっています.作用域を離れると、当然aのコンストラクタを呼び出すべきです.途中で異常またはreturnを投げ出して返すように.
結び目
1.switch-caseのswitchはgotoに相当し、caseはgotoタグに相当します.
2.caseで変数を定義する時は、必ず周りに「.」を加えて、局部的な作用領域を形成しなければなりません.そうでなければ、コンパイルが間違っています.習慣的にはいつもcaseのコードを「.」で囲んで、比較的に便利です.一部の会社のcoding convetionの一つです.
[cpp]view plincopyperint?switch(selector){case selectorual:{int i;}case selectorou:/iはここでは見られません}
3.gotoがオブジェクトの初期化を通ったときに、そのオブジェクトのコンストラクタが呼び出されます.