C w / Blocks: блоки на основе стека выходят из области видимости - PullRequest
8 голосов
/ 16 сентября 2011

В одном из заголовочных файлов Apple для libdispatch, queue.h появляется следующее предупреждение:

// 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.

Как ни крути, я не могу придумать тестслучай, который иллюстрирует эту ошибку.Я могу создавать блоки, которые создаются в стеке, но они (кажется) всегда появляются по уникальным адресам в стеке, даже если они находятся вне области видимости друг друга.

Я представляю, что ответ на этот вопрос прост, но он ускользает от меня.Может ли кто-нибудь заполнить пробелы в моем (ограниченном) понимании?

РЕДАКТИРОВАТЬ : я видел этот ответ, но я не совсем понимаю, как этот экземпляр может перевести мой пример, опубликованный выше.Может кто-нибудь показать мне пример использования if конструкций?

1 Ответ

5 голосов
/ 22 сентября 2011

Чтобы завершить закрытие стека внутри функции:

  • Вы должны убедиться, что закрытие действительно является закрытием стека. Начиная с Apple Clang 2.1, замыкание, которое не ссылается на переменные в его текущем контексте (например, в queue.h), реализовано как глобальное закрытие. Это детали реализации, которые могут варьироваться в зависимости от версии компилятора / компилятора;

  • Компилятор должен генерировать код, который эффективно повторно использует / переписывает область стека, где когда-то существовало замыкание. В противном случае каждый объект внутри этой функции находится по отдельному адресу в фрейме стека функций, что означает, что вы не получите сбой внутри этой функции. Похоже, что Apple Clang 2.1 не использует адреса стековой памяти. GCC 4.6 может использовать их повторно, но не поддерживает замыкания.

Поскольку Apple Clang 2.1 не повторно использует адреса в кадре стека функций, а GCC 4.6 не поддерживает замыкания, из того, что я могу сказать, невозможно сделать этот конкретный пример - внутри функции вызовите стек вне области закрытие - авария.

Я написал более подробный текст об этом в моем блоге .

...