1. ホーム
  2. c++

[解決済み] gotoを使うと変数が漏れる?

2023-01-31 17:54:43

質問

次のことは本当ですか? goto はデストラクタなどを呼び出すことなく、コードのビットを飛び越えるというのは本当ですか?

など。

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

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

ウォンツ x が漏れるのでは?

どのように解決するのですか?

<サブ 警告です。 この回答は C++ に関連するものです のみ Cではルールはかなり異なっています。


しない x が漏れるのでは?

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

というのは神話です。 goto は C++ の組み込みのスコープ機構を上書きできる低レベルの構成要素であるというのは迷信です。(どちらかというと、それは 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-qualified バージョン,または,これらの型の配列でない限り,自動保存期間を持つ変数がスコープ内に存在することは,不正確です. これらの型の cv-qualified バージョン,または前述の型の 1 つを持つ配列で,イニシャライザなしで宣言されている場合を除きます. イニシャライザなしで宣言されています.[..]


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

同様に、自動保存期間を持つオブジェクト ではなく 範囲外 :

goto

struct T { T() { cout << "*T"; } ~T() { cout << "~T"; } }; int main() { { T t; goto lol; } lol: return 0; } // *T~T スコープから出るとき(どのように達成されたとしても),オブジェクト 自動保存期間(3.7.3)を持つオブジェクトで、そのスコープで構築されたものは を持つオブジェクトは,その構築の逆の順序で破壊される。 [..]


結論

上記の仕組みにより [n3290: 6.6/2]: は言語を壊させないようにします。

もちろん、だからといって自動的に goto を使うべきだということではありませんが、これは 一般的な神話が人々に信じさせるほどには、quot; evil" ではないということを意味します。