static int arr [10] адрес памяти всегда заканчивается 060

17

У меня есть программа переменного тока, которая выглядит так

main.c

#include <stdio.h>
#define SOME_VAR 10

static int heap[SOME_VAR];


int main(void) {
    printf("%p", heap);
    return 0;
}

и выводит это, когда я запускаю скомпилированную программу несколько раз

0x58aa7c49060
0x56555644060
0x2f8d1f8e060
0x92f58280060
0x59551c53060
0xd474ed6e060
0x767c4561060
0xf515aeda060
0xbe62367e060

Почему это всегда заканчивается 060? И хранится ли массив в куче?

Изменить: я на Linux и у меня есть ASLR. Я скомпилировал программу используя gcc

linuxlmao
источник
2
Какая операционная система? Какой компилятор?
Эндрю Хенле
2
Переменная не находится в куче, она находится в разделе data или bss адресного пространства программы, см. En.wikipedia.org/wiki/Static_variable . Я предполагаю, что программа всегда будет помещаться по адресу памяти на определенной границе, например, делимой на 0x1000, а переменная размещается компилятором с фиксированным смещением в адресном пространстве программы.
Бодо

Ответы:

15

Адреса различаются из-за ASLR (рамдомизация адресного пространства). Используя это, двоичный файл может отображаться в разных местах в виртуальном адресном пространстве.

Переменная, heapв отличие от ее имени, находится не в куче, а в bss. Поэтому смещение в адресном пространстве является постоянным.

Страницы отображаются с гранулярностью страниц, которая составляет 4096 байт (hex: 0x1000) на многих платформах. По этой причине последние три шестнадцатеричные цифры адреса совпадают.

Когда вы делали то же самое с переменной стека , адрес мог даже меняться в последних цифрах на некоторых платформах (а именно в linux с последними ядрами), потому что стек не только отображается в другом месте, но также получает случайное смещение при запуске.

Ctx
источник
ASLR рандомизируют базу загрузки как я помню. Адрес раздела основан на этом адресе.
Афшин
Я использую книгу об объектно-ориентированном программировании ANSI-C Акселя-Тобиаса Шрайнера. Книга написана примерно в 1993 году. Знаете ли вы, когда память была разной? Если это не так, почему он мог назвать переменную, heapкогда она не в куче?
linuxlmao
4096 переводит в 060 каким-то образом или 0x1000 переводит в 060, иначе я не понимаю, что вы подразумеваете под тем, что является причиной окончания? Я подумал, что это может быть связано с размером массива, который переводится в 060 в шестнадцатеричном виде, например, десятичный
linuxlmao
2
@linuxlmao Смещение составляет, например, 14060, поэтому при добавлении кратного размера страницы (0x1000) остаются три последние цифры 060.
Ctx
4

Если вы используете Windows, причина в структуре PE .

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

Например, это таблица скомпилированной версии Windows , ваш код: раздел были ваш скомпилированный код и содержит ваш переменный. Когда ваш PE загружен в память, разделы загружаются по другому адресу, который возвращается и будет кратным размеру страницы. Но адрес каждой переменной относительно начала раздела, который теперь является размером страницы. Таким образом, вы всегда будете видеть фиксированное число в младших разрядах. Поскольку относительный адрес в начале раздела основан на компиляторе, опциях компиляции и т. Д., Вы увидите разные числа в одном и том же коде, но разные компиляторы, но каждый раз, когда будет напечатано, исправлено.разделы.text.dataheapVirtualAlloc()heap

Когда я компилировал код, я заметил, что heapон помещается в 0x8B0байты после начала .dataраздела. Поэтому каждый раз, когда я запускаю этот код, мой адрес заканчивается 0x8B0.

Afshin
источник
Я использую книгу об объектно-ориентированном программировании ANSI-C Акселя-Тобиаса Шрайнера. Книга написана примерно в 1993 году. Знаете ли вы, когда память была разной? Если это не так, почему он мог назвать переменную, heapкогда она не в куче?
linuxlmao
2
@linuxlmao Возможно, все было иначе. В 1993 году Windows была 16-разрядной операционной системой с сегментацией памяти и всевозможными путаницами. Это была не 32-разрядная архитектура с плоской памятью, как сейчас. Но эти вещи объясняют, почему нецелесообразно задавать / отвечать на общие вопросы о расположении двоичного файла программы в памяти. Поймите, что стандарт языка C гарантирует вам в целом , и это все, что вам нужно знать. Если вы отлаживаете конкретную проблему, беспокойтесь только о фактической разметке, а затем используйте отладчик
Коди Грей
нет, переменная не должна создаваться в куче даже в старых системах, потому что она не была выделена с помощью malloc и имеет статическую продолжительность хранения
phuclv
@Afshin Я обращаюсь к комментарию ОП выше
phuclv
@phuclv извините, потому что вы не упомянули его, я думал, что вы обращаетесь ко мне. :)
Афшин
4

Случилось так, что компилятор установил heapсмещение 0x60 байтов в сегменте данных, который у него есть, возможно, потому, что у компилятора есть некоторые другие вещи в первых байтах 0x60, такие как данные, используемые кодом, который запускает mainподпрограмму. Вот почему вы видите «060»; это именно то, где это произошло, и для этого нет большого значения.

Рандомизация размещения адресного пространства изменяет базовый адрес (а), используемый для различных частей памяти программ, но это всегда происходит в единицах 0x1000 байт (потому что это позволяет избежать проблем с выравниванием и других проблем). Таким образом, вы видите, что адреса колеблются с кратностью 0x1000, но последние три цифры не меняются.

Определение static int heap[SOME_VAR];определяет heapсо статической продолжительностью хранения. Типичные реализации C хранят его в общем разделе данных, а не в куче. «Куча» является неправильным обозначением памяти, которая используется для динамического выделения. (Это неправильно, потому что mallocреализации могут использовать различные структуры данных и алгоритмы, не ограничиваясь кучами. Они могут даже использовать несколько методов в одной реализации.)

Эрик Постпищил
источник