В C фигурные скобки действуют как кадр стека?
Если я создаю переменную в новом наборе фигурных скобок, эта переменная выскочила из стека на закрывающей скобке или она висит до конца функции? Например:
void foo() {
int c[100];
{
int d[200];
}
//code that takes a while
return;
}
будет d занимайте память во время ?
9 ответов:
нет, фигурные скобки не действуют как кадр стека. В C фигурные скобки обозначают только область именования, но ничего не уничтожается и ничего не выскакивает из стека, когда из него выходит управление.
как программист, пишущий код, вы часто можете думать об этом, как будто это кадр стека. Идентификаторы, объявленные в фигурных скобках, доступны только в фигурных скобках, поэтому с точки зрения программиста это похоже на то, что они помещаются в стек, когда они объявляются, а затем выскакивают, когда выход из области видимости. Однако компиляторы не должны генерировать код, который выталкивает/выталкивает что-либо при входе/выходе (и, как правило, они этого не делают).
также обратите внимание, что локальные переменные могут вообще не использовать пространство стека: они могут храниться в регистрах ЦП или в каком-либо другом вспомогательном хранилище или быть полностью оптимизированы.
Итак,
dмассив, в теории, может использовать память для всей функции. Однако компилятор может оптимизировать его или поделиться своей памятью другие локальные переменные, время использования которых не перекрывается.
время, в течение которого переменная на самом деле использование памяти, очевидно, зависит от компилятора (и многие компиляторы не настраивают указатель стека, когда внутренние блоки вводятся и выходят из функций).
однако, тесно связанный, но, возможно, более интересный вопрос заключается в том, разрешен ли программе доступ к этому внутреннему объекту за пределами внутренней области (но в пределах содержащей функции), т. е.:
void foo() { int c[100]; int *p; { int d[200]; p = d; } /* Can I access p[0] here? */ return; }(другими словами: это компилятор разрешено для освобождения
d, даже если на практике большинство нет?).ответ заключается в том, что компилятор и позволило освободить
d, и доступ кp[0]где комментарий указывает на неопределенное поведение (программа не разрешен доступ к внутреннему объекту за пределами внутренней области). Соответствующая часть стандарта C составляет 6.2. 4p5:для такого объекта [один автоматическое запоминающее устройство продолжительность] что делает не имеют тип массива переменной длины, его жизни простирается от входа в блок, с которым он связан пока выполнение этого блока не закончится в любом случае. (Вход в закрытый блок или вызов функции приостанавливается, но не заканчивается, выполнение текущего блок.) Если блок введен рекурсивно, новый экземпляр объект создается каждый раз. Этот первоначальная стоимость объекта сомнительный. Если инициализация является указанного объекта, это выполняется каждый раз, когда объявление достигнутым в ходе выполнения этого блока; в противном случае значение становится неопределенный каждый раз, когда декларация достигнута.
ваш вопрос не достаточно ясен, чтобы ответить однозначно.
С одной стороны, компиляторы обычно не выполняют никакого локального выделения памяти-освобождения для вложенных областей блоков. Локальной памяти обычно выделяется только один раз при входе в функцию и освобождается при выходе из функции.
С другой стороны, когда время жизни локального объекта, размере памяти, занимаемой этим объектом может быть повторно использован для другого локального объекта позже. Например, в этом код
void foo() { { int d[100]; } { double e[20]; } }обоих массивах обычно занимают одну и ту же область памяти, что означает, что общий объем локального хранилища необходимые функции
foo- это все, что необходимо для большой из двух массивов, а не для них обоих одновременно.квалифицируется ли последний как
dпродолжая занимать память до конца функции в контексте вашего вопроса для вас, чтобы решить.
Это зависит от реализации. Я написал короткую программу, чтобы проверить, что делает gcc 4.3.4, и он выделяет все пространство стека сразу в начале функции. Вы можете изучить сборку, которую gcc создает с помощью флага-S.
нет, d [] будет не быть в стеке для остальной части процедуры. Но alloca () - это другое.
Edit: Кристофер Джонсон (а также Саймон и Даниэль) являются право, и мой первоначальный ответ был неправильно. С gcc 4.3.4.на CYGWIN код:
void foo(int[]); void bar(void); void foobar(int); void foobar(int flag) { if (flag) { int big[100000000]; foo(big); } bar(); }выдает:
_foobar: pushl %ebp movl %esp, %ebp movl 0000008, %eax call __alloca cmpl , 8(%ebp) je L2 leal -400000000(%ebp), %eax movl %eax, (%esp) call _foo L2: call _bar leave retЖиви и учись! И быстрый тест, кажется, показывает, что AndreyT также правильно о нескольких распределения.
добавлено намного позже: приведенный выше тест показывает документация gcc не совсем верно. В течение многих лет он говорит (Курсив мой):
" пространство для массива переменной длины освобожден как только имя массива scopeзаканчивается."
Они могут. А может, и нет. Ответ, который я думаю, вам действительно нужен:никогда ничего не предполагайте. современные компиляторы делают все виды архитектуры и реализации конкретной магии. Напишите свой код просто и разборчиво для людей, и пусть компилятор делает хорошие вещи. Если вы пытаетесь кодировать вокруг компилятора, вы просите о неприятностях-и проблема, которую вы обычно получаете в этих ситуациях, обычно ужасно тонкая и трудно диагностируемая.
переменная
dобычно не выскочил из стека. Фигурные скобки не обозначают кадр стека. В противном случае, вы не могли бы сделать что-то вроде этого:char var = getch(); { char next_var = var + 1; use_variable(next_char); }если фигурные скобки вызвали истинный стек push / pop (как вызов функции), то приведенный выше код не будет компилироваться, потому что код внутри фигурных скобок не сможет получить доступ к переменной
varкоторый живет вне фигурных скобок (так же, как подфункция не может напрямую обращаться к переменным в вызывающая функция.) Мы знаем, что это не так.фигурные скобки просто используются для определения объема. Компилятор будет рассматривать любой доступ к" внутренней " переменной из-за пределов окружающих скобок как недопустимый, и он может повторно использовать эту память для чего-то другого (это зависит от реализации). Однако он не может быть удален из стека до тех пор, пока не вернется функция enclosing.
обновление: вот что C spec скажет. По отношению к объекты с автоматическим сроком хранения (раздел 6.4.2):
для объекта, который не имеет типа массива переменной длины, его время жизни продолжается от входа в блок, с которым он связан пока выполнение этого блока не закончится в любом случае.
в том же разделе определяется термин "срок службы" как (акцент мой):
The продолжительность жизни объекта-это часть выполнения программы во время какое хранилище это гарантированный быть зарезервированным для него. Объект существует, имеет постоянный адрес и сохраняет его последнее сохраненное значение на всем протяжении его срок службы. Если на объект ссылаются за пределами его жизненного цикла, то поведение не определено.
ключевое слово здесь, конечно, "с гарантией". Как только вы покинете область внутреннего набора фигурных скобок, время жизни массива закончится. Хранилище может быть или не быть выделено для него (ваш компилятор может повторно использовать пространство для что-то еще), но любые попытки доступа к массиву вызывают неопределенное поведение и приводят к непредсказуемым результатам.
спецификация C не имеет понятия о кадрах стека. Он говорит только о том, как будет вести себя полученная программа, и оставляет детали реализации компилятору (в конце концов, реализация будет выглядеть совсем иначе на БЕССТЕКОЛЬНОМ процессоре, чем на процессоре с аппаратным стеком). В спецификации C нет ничего, что предписывает, где кадр стека будет или не будет конец. Единственный реальные способ узнать-это скомпилировать код на вашем конкретном компиляторе / платформе и изучить полученную сборку. Текущий набор параметров оптимизации вашего компилятора, вероятно, также сыграет свою роль в этом.
если вы хотите убедиться, что массив
dбольше не съедает память во время выполнения кода, Вы можете либо преобразовать код в фигурных скобках в отдельную функцию или явноmallocиfreeпамять вместо использования автоматическое хранение.
Я считаю, что он выходит за рамки, но не всплывает из стека, пока функция не вернется. Таким образом, он по-прежнему будет занимать память в стеке до тех пор, пока функция не будет завершена, но не будет доступна после первой закрывающей фигурной скобки.
там уже было дано много информации о стандарте, указывая, что это действительно конкретной реализации.
Итак, один эксперимент может представлять интерес. Если мы попробуем следующий код:
#include <stdio.h> int main() { int* x; int* y; { int a; x = &a; printf("%p\n", (void*) x); } { int b; y = &b; printf("%p\n", (void*) y); } }используя gcc мы получаем здесь два раза один и тот же адрес: Coliro
но если мы попробуем следующий код:
#include <stdio.h> int main() { int* x; int* y; { int a; x = &a; } { int b; y = &b; } printf("%p\n", (void*) x); printf("%p\n", (void*) y); }используя gcc мы получаем здесь два разных адреса: Coliro
таким образом, вы не можете быть действительно уверены, что происходит.
Comments