C w/块:基于堆栈的块超出范围

C w/块:基于堆栈的块超出范围,c,objective-c-blocks,grand-central-dispatch,C,Objective C Blocks,Grand Central Dispatch,在苹果的libdispatch头文件之一中,出现以下警告: // 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");

在苹果的
libdispatch
头文件之一中,出现以下警告:

// 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.
尽管我可能会尝试,但我不能拿出一个测试用例来演示这个bug。我可以创建在堆栈上实例化的块,但它们(似乎)总是出现在堆栈上的唯一地址,即使它们彼此超出范围

我想答案很简单,但我想不起来。有人能填补我(有限的)理解上的空白吗


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

来破坏函数中的堆栈闭包:

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

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

由于Apple Clang 2.1不重用函数堆栈框架中的地址,GCC 4.6也不支持闭包,因此我可以告诉您,不可能在函数内部调用超出范围的堆栈闭包,使这个特定示例崩溃


我在上写了一篇更详细的文章。

您发布的链接与另一个问题有关,即在存在可变变量的情况下,闭包的行为似乎很奇怪。请参阅问题,这与JavaScript中的问题完全相同。然而,他们似乎犯了这一评论所警告的错误。现在块会自动复制自己吗?我也想知道。我试过一点,但总是得到与您相同的结果,块结构似乎在函数范围内。也许很多人被咬了,他们改变了这一点?谢谢你的回应,巴顿。你是我希望能回答我问题的人之一我意识到块必须捕获状态(否则它们将被呈现为全局状态)才能放在堆栈上,但我发现与您一样——堆栈地址不会被闭包重用。@Sed更一般——堆栈地址显然不会被任何类型的对象重用,包括闭包。这实际上意味着在函数堆栈框架中创建的所有内容在函数结束之前都是活动的。我不确定Clang是否有意避免这种情况,也不确定它是否会在将来实现。我正计划用一个“啊哈!”,因为我发现堆栈地址确实被
long
类型重用,但我将编译器设置为GCC 4.2。哦。@Sed,而且它们不会在叮当声中重复使用?不会,至少在我有限的测试中不会。