2010-03-15 3 views
6

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

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

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

Testrun: -

[email protected]:~/c$ gcc test.c 
[email protected]:~/c$ ./a.out 
0x7fffc0b8e138 
0x7fffc0b8e138 
0x7fffc0b8e138 
[email protected]:~/c$ 
+0

Я отправил неправильный вопрос по ошибке .. Мой плохой .. извините ..Я обновил вопрос –

+0

@ Gardener: он будет компилироваться отлично. 'void *' будет автоматически изменяться. –

+0

Теперь вы изменили вопрос. Пробовали ли вы его проверить, верно ли ваше первоначальное утверждение? Это даже не правильный код, поэтому ответ на это почти наверняка нет. – Clifford

ответ

6

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

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

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

НКУ преобразует это в следующий код 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, ассемблерный код не настроил стек, чтобы выделить для него память, о которой уже позаботились раньше.

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

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

+0

Хороший ответ. Вот такой ответ я хотел !!! –

+0

На всякий случай, если кто-то должен знать, как получить код сборки. Используйте $ gcc -S foo.c –

+0

@Andrew - Pls расскажет хороший источник, чтобы узнать язык ассемблера. Я знаю только LC3. Теперь я хочу понять ассемблерные коды, дабы сказать Core2Quad. –

12

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

+0

Но, я получаю тот же адрес каждый раз .. Почему? –

+1

Потому что это одна и та же переменная каждый раз. –

+5

@Adam: Это не «действительно» одна и та же переменная каждый раз, она находится в одном месте каждый раз (в этой реализации и практически в любой правдоподобной реализации C) –

1

Вы не malloc-ing. Его адрес стека так же неизменен, потому что он всегда находится в одном и том же месте стека раз и навсегда.

2

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

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

См. Hardware Stack в Википедии для более подробной информации.

6

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

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

2

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

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

4

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

-1

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

Если вы освободите j в конце цикла, вы можете получить то же поведение, что и раньше, в зависимости от реализации malloc и бесплатно.

Редактировать: вы можете перепроверять напечатанные значения с помощью этого кода.

+0

Вы не должны освобождать j в этом случае, так как это wasn Динамически выделяется. –

+0

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

0

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

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

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

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

0

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

Это зависит от используемого 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 может всегда меняться.

Смежные вопросы