【问题标题】:C w/ Blocks: Stack-based blocks going out of scopeC w/ Blocks:超出范围的基于堆栈的块
【发布时间】:2011-09-16 01:36:10
【问题描述】:

libdispatchqueue.h 的 Apple 头文件之一中,出现以下警告:

// The declaration of a block allocates storage on the stack. 
// Therefore, this is an invalid construct:

dispatch_block_t block;

if (x) {
    block = ^{ printf("true\n"); };
} else {
    block = ^{ printf("false\n"); };
}
block(); // unsafe!!!

// What is happening behind the scenes:

if (x) {
    struct Block __tmp_1 = ...; // setup details
    block = &__tmp_1;
} else {
    struct Block __tmp_2 = ...; // setup details
    block = &__tmp_2;
}

// As the example demonstrates, the address of a stack variable is 
// escaping the scope in which it is allocated. That is a classic C bug.

尽我所能,我想不出一个可以说明这个错误的测试用例。我可以创建在堆栈上实例化的块,但它们(似乎)总是出现在堆栈上的唯一地址处,即使彼此超出范围也是如此。

我想这个问题的答案很简单,但它让我无法理解。谁能填补我(有限)理解中的空白?

编辑:我看到了this 的回复,但我不太明白该实例如何转化为我上面发布的示例。有人可以给我看一个使用if 构造的例子吗?

【问题讨论】:

  • 您发布的链接与另一个问题有关,即在存在可变变量的情况下,闭包似乎表现得很奇怪。请参阅问题"Javascript: closure of loop?",这与 JavaScript 中的问题完全相同。然而,他们似乎犯了这个评论所警告的错误。这些天块会自动复制自己吗?我也想知道。
  • 我尝试了一点,但总是得到和你一样的结果,块结构似乎在函数范围内。也许很多人被咬了,他们就这样改变了?

标签: c objective-c-blocks grand-central-dispatch


【解决方案1】:

为了使函数内部的堆栈闭包崩溃:

  • 您需要确保闭包确实是堆栈闭包。从 Apple Clang 2.1 开始,在当前上下文中不引用变量的闭包(如 queue.h 中的那个)被实现为全局闭包。这是一个实现细节,可能因不同的编译器/编译器版本而异;

  • 编译器必须发出有效重用/重写闭包曾经存在的堆栈区域的代码。否则,该函数内的每个对象都位于函数堆栈帧中的不同地址,这意味着您不会在该函数内发生崩溃。 Apple Clang 2.1 似乎没有重用堆栈内存地址。 GCC 4.6 可以重用它们,但不支持闭包。

由于 Apple Clang 2.1 不重用函数堆栈框架中的地址,而 GCC 4.6 不支持闭包,据我所知,不可能制作这个特定示例 - 在函数内部,调用超出范围的堆栈关闭——崩溃。

我在my blog 上写了一篇更详细的文字。

【讨论】:

  • 感谢您的回复,巴伐利亚。你是我希望能回答我的问题的人之一。 :) 我已经意识到块必须捕获状态(否则它们会被渲染为全局)才能放置在堆栈上,但我发现和你一样——堆栈地址不会被用于闭包。
  • @Sed 它更通用——堆栈地址显然不会被任何类型的对象重用,包括闭包。这实际上意味着在函数堆栈帧中创建的所有内容在函数结束之前都是有效的。我不确定 Clang 是否有意避免这种情况,或者将来是否会实施。
  • 我打算用“啊哈!”来跟进,发现堆栈地址确实被long 类型重用,但我将编译器设置为 GCC 4.2。哦。
  • @Sed 他们不会被 Clang 重用吗?
  • 不,至少在我有限的测试中没有。
猜你喜欢
  • 2014-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-26
  • 2011-01-25
  • 2018-02-23
  • 2015-09-27
  • 1970-01-01
相关资源
最近更新 更多