Тогда почему бы стек не начинать с адреса 0xFFFFFFFFFFFFFFFC? И не было бы никакого ограничения в 1 Мб.
Раньше, кстати, так и делали (с поправкой на разрядноcь адресов). Можно просто с нуля - при росте стека его значение сначала уменьшается, потом заполняется данными.
Именно это и является причиной того, что стек в x86/64 (и ARM) растет сверху вниз: снизу ели память данные программы, сверху - стек. Этот атавизм сохранился до сих пор, хотя уже больше 20 лет, с появлением вытесняющей многозадачности и необходимостью размещать несколько стеков одновременно, принципиально перестал быть полезным.
Впрочем, пробегали несколько извращенные подходы использования атавизма - например, последовательное чтение данных из массивов командами
pop (это оказывалось быстрее классических способов).
Конкретно в данном случае вмешивается ОС с механизмом подкачки виртуальных страниц, т.е. память-то выделяется (в x64 ОС), но не физическая, а виртуальная, и физически существуют лишь те страницы памяти, в которые была реальная запись
Скорее, вообще никакая не выделяется. Точнее, выделяется она, если так можно сказать, в момент создания потока. В момент входа в такую экзотическую функцию просто уменьшается значение стека, перепрыгнув кучу страниц. Пока это значение не выйдет за пределы допустимой области, или массив не начнет реально использоваться, никто ничего не заметит.