Распределение памяти для глобальных и локальных переменных
Я узнал, что память для глобальных переменных выделяется при запуске программы, тогда как память для локальных переменных выделяется всякий раз, когда выполняется вызов функции.
Случай 1:
Я объявила глобальный целочисленный массив размером 63500000, а используемая память - 256 МБ
Идеальная ссылка
include <stdio.h>
int a[63500000];
int main()
{
printf ("This code requires about 250 MB memory\n");
return 0;
}
Случай 2:
Я объявил локальный целочисленный массив одинакового размера в main(), а используемая память - 1,6 МБ
Идеальная ссылка
#include <stdio.h>
int main()
{
int a[63500000]= {1,5,0};
printf ("This code requires only 1.6 MB \n");
//printf ("%d\n", a[0]);
return 0;
}
Случай 3:
Я объявил локальный целочисленный массив одинакового размера в другой функции, а используемая память - 1,6 МБ
Идеальная ссылка
#include <stdio.h>
void f()
{
int a[63500000];
}
int main()
{
f();
return 0;
}
Пожалуйста, объясните, почему разница в используемой памяти или неправильная концепция распределения памяти?
Ответы
Ответ 1
Прежде всего: компилятор ideone - это GCC.
Итак, что делает GCC при компиляции этого?:
void foo ()
{
int a[63500000];
}
gcc -S -O2 foo.c
генерирует:
foo:
pushl %ebp
movl %esp, %ebp
popl %ebp
ret
то есть. ничего не выделяется в стеке.
Массив просто оптимизирован GCC, потому что он никогда не используется.
GCC не будет делать это с глобальным, потому что возможно, что глобальный используется в другой единице компиляции, и поэтому он не уверен, что он никогда не используется. Кроме того: глобальный не находится в стеке (поскольку он является глобальным).
Теперь давайте посмотрим, что произойдет, когда вы на самом деле используете локальный массив:
int bar (int a, int b, int c)
{
int f[63500000];
f[a] = 9;
f[b] = 7;
return f[c];
}
Все очень разные:
bar:
pushl %ebp
movl %esp, %ebp
subl $254000000, %esp
movl 8(%ebp), %eax
movl $9, -254000000(%ebp,%eax,4)
movl 12(%ebp), %eax
movl $7, -254000000(%ebp,%eax,4)
movl 16(%ebp), %eax
movl -254000000(%ebp,%eax,4), %eax
leave
ret
Эта строка: subl $254000000, %esp
соответствует размеру массива. то есть память выделяется в стеке.
Теперь, если я попытался использовать функцию bar
в программе:
int bar (int a, int b, int c)
{
int f[63500000];
f[a] = 9;
f[b] = 7;
return f[c];
}
int main (void)
{
return bar (0, 0, 0);
}
Мы уже видели, что функция bar
выделяет 250 мегабайт в стеке. В моей установке GNU/Linux по умолчанию размер стека ограничен 8 МБ. Поэтому, когда программа запускается, она вызывает "Ошибка сегментации". Я могу увеличить его, если захочу, выполнив в оболочке следующее:
ulimit -s 1000000 #i.e. allow stack size to grow close to 1GB
Затем я могу запустить программу, и она действительно запустится.
Причина, по которой это происходит на веб-сайте ideone, заключается в том, что они ограничивают размер стека при выполнении программ (и они должны, в противном случае злонамеренные пользователи могут испортить свою систему).
Ответ 2
Случаи 2, 3
Переменные, которые вы определяете внутри функций, выделяются в стеке. Это означает, что связанная память очищается (стек выталкивается), когда функция завершается.
Случай 1
Переменные, определенные в глобальной области, выделяются в сегменте данных (или, как правило, в пространстве памяти, запрошенном из операционной системы), который существует для времени жизни процесса.
Дополнительно
Память, выделенная с помощью malloc, выделяется из кучи и остается выделенной до тех пор, пока явно не будет выпущена свободная.
Обратите внимание, что современная ОС может обеспечить адресное пространство, запрошенное программой, но физически не вернуть это адресное пространство с ОЗУ, пока физический доступ к памяти (или части памяти, часто называемой страницей) физически недоступен.
Ответ 3
case 2
и case 3
приведет к переполнению стека, поскольку вы запрашиваете 64 МБ стековой памяти, в которой ваш стек равен обычно 8 МБ в Linux. это приведет к случайным плохим вещам и/или основным дампам и сбоям.
этот ответ в значительной степени объясняет различные разделы адресного пространства процесса (.text,.bss,.data) и способы выполнения различных распределений переменных.