goto 可以在不调用析构函数的情况下跳转函数吗?

发布于 2024-12-03 18:47:42 字数 204 浏览 2 评论 0原文

goto 是否真的可以在不调用析构函数和其他东西的情况下跳过代码段?

例如

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

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

x 不会被泄露吗?

Is it true that goto jumps across bits of code without calling destructors and things?

e.g.

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

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

Won't x be leaked?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

够运 2024-12-10 18:47:42

警告:此答案仅适用于 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 限定版本
这些类型,或前述类型之一的数组,并被声明
没有初始化器。 [..]


3. 跳转遵守其他对象的范围

同样,具有自动存储期限的对象当您进入范围之外时“泄露”:

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 来解决任何给定的问题,但它确实意味着它并不像常见的神话引导人们相信。

Warning: This answer pertains to C++ only; the rules are quite different in C.


Won't x be leaked?

No, absolutely not.

It is a myth that goto is some low-level construct that allows you to override C++'s built-in scoping mechanisms. (If anything, it's longjmp that may be prone to this.)

Consider the following mechanics that prevent you from doing "bad things" with labels (which includes case labels).


1. Label scope

You can't jump across functions:

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

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

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

[n3290: 6.1/1]: [..] The scope of a label is the function in which
it appears. [..]


2. Object initialisation

You can't jump across object initialisation:

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

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

If you jump back across object initialisation, then the object's previous "instance" is destroyed:

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]: [..] Transfer out of a loop, out of a block, or back
past an initialized variable with automatic storage duration involves
the destruction of objects with automatic storage duration that are in
scope at the point transferred from but not at the point transferred
to. [..]

You can't jump into the scope of an object, even if it's not explicitly initialised:

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

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

... except for certain kinds of object, which the language can handle regardless because they do not require "complex" construction:

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

// OK

[n3290: 6.7/3]: It is possible to transfer into a block, but not in
a way that bypasses declarations with initialization. A program that
jumps from a point where a variable with automatic storage duration
is not in scope to a point where it is in scope is ill-formed unless
the variable has scalar type, class type with a trivial default
constructor and a trivial destructor, a cv-qualified version of one of
these types, or an array of one of the preceding types and is declared
without an initializer. [..]


3. Jumping abides by scope of other objects

Likewise, objects with automatic storage duration are not "leaked" when you goto out of their scope:

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

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

lol:
   return 0;
}

// *T~T

[n3290: 6.6/2]: On exit from a scope (however accomplished), objects
with automatic storage duration (3.7.3) that have been constructed in
that scope are destroyed in the reverse order of their construction.
[..]


Conclusion

The above mechanisms ensure that goto doesn't let you break the language.

Of course, this doesn't automatically mean that you "should" use goto for any given problem, but it does mean that it is not nearly as "evil" as the common myth leads people to believe.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文