Одно и то же пространство памяти выделяется снова и снова - PullRequest
6 голосов
/ 15 марта 2010

В каждой итерации цикла, переменная j объявляется снова и снова. Тогда почему его адрес остается таким же ?

  • Разве не следует каждый раз давать какой-нибудь случайный адрес?
  • Зависит ли этот компилятор?
#include<stdio.h>
#include<malloc.h>

int main()
{
    int i=3;
    while (i--)
    {
        int j;
        printf("%p\n", &j);
    }
    return 0;
}

Testrun: -

shadyabhi@shadyabhi-desktop:~/c$ gcc test.c
shadyabhi@shadyabhi-desktop:~/c$ ./a.out
0x7fffc0b8e138
0x7fffc0b8e138
0x7fffc0b8e138
shadyabhi@shadyabhi-desktop:~/c$

Ответы [ 11 ]

12 голосов
/ 15 марта 2010

Это память в стеке.Он не выделяется из кучи.Стек не изменился бы в этом цикле.

6 голосов
/ 15 марта 2010

Причина, по которой адрес j никогда не меняется, заключается в том, что компилятор выделяет память для j в стеке при вводе функции, а не когда j входит в область действия.

Как всегда, взгляд на некоторый ассемблерный код может помочь объяснить концепцию. Возьмите следующую функцию: -

int foo(void)
   {
   int i=3;
   i++;
      {
      int j=2;
      i=j;
      }
   return i;
   }

gcc преобразует это в следующий код сборки x86: -

foo:
    pushl   %ebp                 ; save stack base pointer
    movl    %esp, %ebp           ; set base pointer to old top of stack
    subl    $8, %esp             ; allocate memory for local variables
    movl    $3, -4(%ebp)         ; initialize i
    leal    -4(%ebp), %eax       ; move address of i into eax
    incl    (%eax)               ; increment i by 1
    movl    $2, -8(%ebp)         ; initialize j
    movl    -8(%ebp), %eax       ; move j into accumulator
    movl    %eax, -4(%ebp)       ; set i to j
    movl    -4(%ebp), %eax       ; set the value of i as the function return value
    leave                        ; restore stack pointers
    ret                          ; return to caller

Давайте пройдемся по этому ассемблерному коду. Первая строка сохраняет текущий базовый указатель стека, чтобы его можно было восстановить при выходе из функции, вторая строка устанавливает текущую вершину стека в качестве нового базового указателя стека для этой функции.

Третья строка - это та, которая выделяет память в стеке для всех локальных переменных. Инструкция subl $8, %esp вычитает 8 из текущей вершины указателя стека, регистра esp. Стеки растут в памяти, поэтому эта строка кода увеличивает объем памяти в стеке на 8 байт. В этой функции у нас есть два целых числа, i и j, каждому из которых требуется 4 байта, поэтому он выделяет 8 байтов.

Строка 4 инициализирует i до 3 путем прямой записи по адресу в стеке. Строки 5 и 6 затем загружают и увеличивают i. Строка 7 инициализирует j, записывая значение 2 в память, выделенную для j в стеке. Обратите внимание, что когда j вошло в область видимости в строке 7, код сборки не изменил стек для выделения памяти для него, о чем уже позаботились ранее.

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

Я уверен, что вы сами сможете понять, что делает остальная часть кода сборки, если не оставите комментарий, и я проведу вас через него.

6 голосов
/ 15 марта 2010

Почему это должно быть иначе? Компилятору требуется место в стеке для хранения целых чисел, и каждый раз, когда он проходит через цикл, одно и то же место доступно.

Кстати, вы вообще не используете malloc вообще. j хранится в стеке.

4 голосов
/ 15 марта 2010

j и i размещаются в стеке, а не в куче или свободном хранилище (что потребовало бы malloc или new соответственно). Стек помещает следующую переменную в детерминистическое место (вершину стека), и поэтому он всегда имеет один и тот же адрес. Хотя, если вы работаете в оптимизированном режиме, переменная, вероятно, никогда не будет «освобождена», то есть размер стека не изменяется во всей вашей программе, потому что это просто пустая трата циклов.

2 голосов
/ 15 марта 2010

На самом деле вы не используете malloc, так в чем же проблема?

Переменная является локальной для функции, и ее пространство зарезервировано в стеке во время компиляции. Так почему она должна перераспределять ее на каждой итерации? Только потому, что он объявлен внутри цикла?

2 голосов
/ 15 марта 2010

j выделяется в стеке, поэтому во время одного вызова этой функции он всегда будет иметь один и тот же адрес.

Если бы вы вызвали main() из этого цикла, у «внутреннего» * ​​1005 * j будет другой адрес, так как он будет выше в стеке.

Подробнее см. Аппаратный стек в Википедии.

1 голос
/ 15 марта 2010

Это объявлено в цикле, как вы говорите, , но оно выходит из области видимости и «уничтожается» в конце каждой итерации (то есть не входит в область и не существует, условие цикла проверяется). Поэтому вполне допустимо повторное использование одного и того же местоположения стека (на самом деле это было бы ошибкой, если бы это было не так).

1 голос
/ 15 марта 2010

Вы не занимаетесь. Это адрес стека, поэтому он всегда один и тот же, потому что он всегда один и тот же в стеке.

0 голосов
/ 15 марта 2010

Как уже говорили другие ответы, вы не выделяете здесь ничего, кроме как в стеке. Но даже если вы измените код следующим образом, он не обязательно изменит адрес выделения.

Это зависит от используемой libc, там обычно находится malloc, но некоторые приложения (особенно firefox) переопределяют ее для своего использования (проблемы фрагментации памяти и т. Д.).

#include<stdio.h>
#include<malloc.h>

int main()
{
    int i=3;
    while (i--)
    {
        int *j = (int *) malloc(sizeof(int));
        printf("%p\n", j);
        free (j);
    }
    return 0;
}

если вы закомментируете бесплатное (j), вы заметите, что адрес j действительно меняется. Но в зависимости от вашего libc адрес j всегда может измениться.

0 голосов
/ 15 марта 2010

Подсказка: как вы думаете, что это будет делать?

#include<stdio.h>
#include<malloc.h>

int main()
{
    int i=3;
    while (i--)
    {
        int j = 42;
        printf("%p\n", &j);
    }
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...