Почему мы не можем объявить массив, например типа данных int, любого размера в пределах ограничения памяти?

int A[10000000]; //This gives a segmentation fault
int *A = (int*)malloc(10000000*sizeof(int));//goes without any set fault.

Теперь мой вопрос, просто из любопытства, в том, что, если в конечном итоге мы сможем выделить более высокое пространство для наших структур данных, скажем, например, BST и связанные списки, созданные с использованием подхода указателей в C, не имеют такого ограничения памяти ( если общий размер не превышает размер ОЗУ для нашей машины) и, например, во втором выражении выше объявления типа указателя, почему мы не можем объявить массив с более высоким размером (пока он не достигнет предела памяти!!)... Это потому, что выделенное пространство смежно в массиве статического размера?. Но тогда откуда мы получаем гарантию, что в следующих 1000000 словах в ОЗУ не будет работать другой кусок кода...??

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

Ответ 1

Во-первых, в типичной современной ОС с виртуальной памятью (Linux, Windows и т.д.) объем оперативной памяти не имеет никакого значения. Ваша программа работает с виртуальной памятью, а не с ОЗУ. ОЗУ - это только кеш для доступа к виртуальной памяти. Абсолютным ограничивающим фактором для максимального размера массива является не ОЗУ, а размер доступного адресного пространства. Адресное пространство - это ресурс, о котором вам нужно беспокоиться в ОС с виртуальной памятью. В 32-разрядных операционных системах у вас есть 4 гигабайта адресного пространства, часть из которых предназначена для различных бытовых нужд, а остальная часть доступна вам. В 64-битных операционных системах у вас теоретически имеется 16 эксабайт адресного пространства (меньше, чем в практических реализациях, поскольку для представления адреса обычно используются менее 64 бит), которые могут восприниматься как практически неограниченные.

Во-вторых, количество доступного адресного пространства в типичной реализации C/С++ зависит от типа памяти. Там статическая память, есть автоматическая память, есть динамическая память. Пределы адресного пространства для каждого типа памяти предварительно задаются компилятором заранее. Что вызывает вопрос: где вы объявляете свой большой массив? Какой тип памяти? Автоматический? Статическая? Вы не предоставили никакой информации, но это абсолютно необходимо. Если вы пытаетесь объявить его как локальную переменную (автоматическую память), то неудивительно, что она не работает, поскольку автоматическая память (так называемая "стековая память" ) имеет очень ограниченное адресное пространство, назначенное ей. Ваш массив просто не подходит. Между тем malloc выделяет динамическую память, которая обычно имеет наибольшее количество адресного пространства.

В-третьих, многие компиляторы предоставляют вам параметры, которые управляют начальным распределением адресного пространства между различными типами памяти. Вы можете запросить гораздо больший размер стека для своей программы, манипулируя такими параметрами. Вполне возможно, вы можете запросить стек настолько большим, чтобы ваш локальный массив не входил в него без каких-либо проблем. Но на практике по понятным причинам очень мало смысла объявлять огромные массивы локальными переменными.

Ответ 2

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

platform    default size       
=====================================
SunOS/Solaris  8172K bytes
Linux          8172K bytes
Windows        1024K bytes
cygwin         2048K bytes

Связанная статья также отмечает, что размер стека может быть изменен, например, в Linux, одним из возможных способов из оболочки перед запуском вашего процесса будет:

ulimit -s 32768 # устанавливает размер стека до 32 Мбайт.

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

Ответ 3

Я ДУМАЮ, что вам не хватает разницы между общей памятью и объемом памяти программ. Ваша программа работает в среде, созданной вашей операционной системой. Он предоставляет определенную область памяти программе, и программа должна попытаться справиться с этим.

Ловушка: ваш компилятор не может знать 100% этого диапазона.

Это означает, что ваш компилятор будет успешно создан, и он будет ЗАПРОСИТЬ, что много места в памяти, когда придет время сделать вызов malloc (или переместить указатель стека при вызове функции). Когда функция вызывается (создание фрейма стека), вы получите ошибку сегментации, вызванную переполнением стека. Когда вызывается malloc, вы не получите segfault, если не попытаетесь использовать память. (Если вы посмотрите на man-страницу для malloc(), вы увидите, что она возвращает NULL, когда памяти недостаточно.)

Чтобы объяснить два отказа, вашей программе предоставляется два пространства памяти. Стек и куча. Память, выделенная с помощью malloc(), выполняется с использованием системного вызова и создается в куче вашей программы. Это динамически принимает или отклоняет запрос и возвращает либо начальный адрес, либо NULL, в зависимости от успеха или неудачи. Стек используется при вызове новой функции. Комната для всех локальных переменных выполняется в стеке, это делается с помощью инструкций программы. Вызов функции не может просто FAIL, так как это полностью разрушит поток программы. Это заставляет систему сказать: "Теперь вы переступаете" и segfault, останавливая выполнение.