gotoリーク変数を使用しますか?


94

gotoデストラクタなどを呼び出さずにコードの一部をジャンプするのは本当ですか?

例えば

void f() {
   int x = 0;
   goto lol;
}

int main() {
   f();
lol:
   return 0;
}

ではないだろうx漏洩しますか?


関連:stackoverflow.com/questions/1258201/…(しかし、私は最初から、きれいにそれをやりたかった!)
オービットでの軽さのレース

15
どういう"Won't x be leaked"意味ですか?の型xは組み込みデータ型です。もっと良い例を選んでみませんか?
Nawaz、2011

2
@Nawaz:この例は、現状のまま完璧です。ほとんどの人がについて話をするたびにgoto、自動ストレージ期間変数でさえも何らかの形で「リーク」されていると考えています。あなたと私が別の方法で知っていることは完全にポイントの外です。
Orbitの軽量レース、2011

1
@David:変数に自明でないデストラクタがある場合、この質問の方がはるかに意味があることに同意します。トマラックの答えを調べて、そのような例を見つけました。さらに、int漏れはありませんが、漏れることがあります。例:をvoid f(void) { new int(5); }リークしintます。
Ben Voigt、2011

質問を次のようなものに変更しないのはなぜですか?「与えられた例では、コード実行パスは、スタックやその他の関数からの戻り機能をクリアせずにf()からmain()に転送されますか?デストラクタをと呼ばれていますか?Cでも同じですか?」どちらも、誤解の可能性を回避しながら、質問の意図を維持しますか?
ジャックV.

回答:


210

警告:この回答はC ++にのみ該当します。規則はCではかなり異なります。


ではないだろうx漏洩しますか?

いいえ、絶対にありません。

これは、gotoC ++の組み込みスコープメカニズムをオーバーライドできるようにする低レベルの構造であるという神話です。(どちらかと言えば、longjmpこの傾向がある可能性があります。)

ラベル(ラベルを含むcase)を使用して「悪いこと」を実行できないようにする次のメカニズムを検討してください。


1.ラベルのスコープ

関数間をジャンプすることはできません。

void f() {
   int x = 0;
   goto lol;
}

int main() {
   f();
lol:
   return 0;
}

// error: label 'lol' used but not defined

[n3290: 6.1/1]:[..]ラベルのスコープは、ラベルが表示される機能です。[..]


2.オブジェクトの初期化

オブジェクトの初期化を飛び越えることはできません。

int main() {
   goto lol;
   int x = 0;
lol:
   return 0;
}

// error: jump to label ‘lol’
// error:   from here
// error:   crosses initialization of ‘int x’

あなたがジャンプする場合は、バックオブジェクトの初期化を越え、その後、オブジェクトの前の「インスタンス」が破壊されました

struct T {
   T() { cout << "*T"; }
  ~T() { cout << "~T"; }
};

int main() {
   int x = 0;

  lol:
   T t;
   if (x++ < 5)
     goto lol;
}

// Output: *T~T*T~T*T~T*T~T*T~T*T~T

[n3290: 6.6/2]:[..]ループ外、ブロック外、または自動ストレージ期間のある初期化された変数を越えて転送すると、転送元のポイントではスコープ内にあるが、転送先のポイントではない、自動ストレージ期間のあるオブジェクトが破棄されます。 。[..]

明示的に初期化されていなくても、オブジェクトのスコープにジャンプすることはできません。

int main() {
   goto lol;
   {
      std::string x;
lol:
      x = "";
   }
}

// error: jump to label ‘lol’
// error:   from here
// error:   crosses initialization of ‘std::string x’

... 「複雑な」構造を必要としないため、言語が関係なく処理できる特定の種類のオブジェクトを除く:

int main() {
   goto lol;
   {
      int x;
lol:
      x = 0;
   }
}

// OK

[n3290: 6.7/3]:ブロックに転送することは可能ですが、初期化で宣言をバイパスする方法ではできません。自動ストレージ期間を持つ変数がスコープ内にないポイントからスコープ内にあるポイントにジャンプするプログラムは、変数がスカラー型、自明なデフォルトコンストラクターおよび自明なデストラクタを持つクラス型でない限り、形式が正しくありません。これらのタイプのいずれかのcv修飾バージョン、または前述のタイプのいずれかの配列であり、初期化子なしで宣言されています。[..]


3.ジャンプは他のオブジェクトのスコープに従います

同様に、自動記憶域期間を持つオブジェクトはありませんが、「漏れた」ときにgoto出て、その範囲の

struct T {
   T() { cout << "*T"; }
  ~T() { cout << "~T"; }
};

int main() {
   {
      T t;
      goto lol;
   }

lol:
   return 0;
}

// *T~T

[n3290: 6.6/2]:スコープからの出口で(達成された場合)、そのスコープで作成された自動ストレージ期間(3.7.3)のオブジェクトは、作成とは逆の順序で破棄されます。[..]


結論

上記のメカニズムによりgoto、言語を壊すことがなくなります。

もちろん、これは自動的にあなたが使用「すべき」という意味ではありませんgoto任意の与えられた問題のために、それはありません、それはほとんどの人が信じて、共通の神話リードとして「悪」としてではないことを意味します。


8
Cはこれらすべての危険なことの発生を防ぐわけではないことに気付くでしょう。
ダニエル

13
@Daniel:質問と回答はC ++について非常に具体的ですが、公平な点です。たぶん、CとC ++が同じであるという神話を払拭する別のFAQがあるかもしれません;)
オービットの軽さの

3
@Tomalak:私たちはここで反対しているとは思いません。SOに関する回答の多くは、どこかに明示的に文書化されています。私はちょうどこの答えを見て、それはC ++で動作するかどうか、それはCで同様に動作する必要があることを前提とするCプログラマのための魅力的かもしれないとポイントを作っていた
ダニエル

2
また、これらの初期化の飛び越しはすべて、ケースラベルでも同じであることを追加することもできます。
PlasmaHH、2011

12
うわー、C ++のセマンティクスがgotoのために壊れていると思いましたが、驚くほど正気です!すばらしい答えです。
Joseph Garvin 2012年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.