Що насправді означає «Пам'ять, виділена під час компіляції»?


159

У мовах програмування, таких як C і C ++, люди часто посилаються на статичне та динамічне розподіл пам'яті. Я розумію це поняття, але фраза "Вся пам'ять була виділена (зарезервована) під час компіляції" завжди мене бентежить.

Компіляція, наскільки я розумію, перетворює код C / C ++ високого рівня в машинну мову та виводить виконуваний файл. Як пам'ять "виділяється" у складеному файлі? Чи не завжди пам'ять виділяється в оперативній пам'яті з усіма елементами управління віртуальною пам'яттю?

Чи не розподіл пам’яті за визначенням не є концепцією виконання?

Якщо я зроблю статично виділену змінну розміром 1 КБ у своєму коді C / C ++, чи збільшить це розмір виконуваного файлу на ту ж суму?

Це одна зі сторінок, де словосполучення використовується під заголовком «Статичне виділення».

Назад до основ: Розподіл пам’яті, прогулянка по історії


код і дані повністю відокремлені в більшості сучасних архітектур. в той час як вихідні файли містять обидва кодові дані в одному місці, в кошику є посилання лише на дані. Це означає, що статичні дані у джерелі вирішуються лише як посилання.
Холті Паул Ттіопік

Відповіді:


184

Пам'ять, виділена під час компіляції, означає, що компілятор вирішує в час компіляції, де певні речі будуть розподілені всередині карти пам'яті процесу.

Наприклад, розглянемо глобальний масив:

int array[100];

Компілятор знає в час компіляції розмір масиву і розмір int, тому він знає весь розмір масиву під час компіляції. Також глобальна змінна за замовчуванням має тривалість статичної пам’яті: вона розподіляється в області статичної пам’яті простору технологічної пам’яті (розділ .data / .bss). Враховуючи цю інформацію, компілятор вирішує під час компіляції, в якій адресі цієї статичної області пам'яті буде масив .

Звичайно, що адреси пам'яті - це віртуальні адреси. Програма передбачає, що у неї є власний простір пам'яті (наприклад, від 0x00000000 до 0xFFFFFFFF). Ось чому компілятор міг робити припущення типу "Гаразд, масив буде за адресою 0x00A33211". Під час виконання ці MMU та ОС перетворюють адреси на реальні / апаратні адреси.

Значення ініціалізованих речей статичного зберігання дещо відрізняються. Наприклад:

int array[] = { 1 , 2 , 3 , 4 };

У нашому першому прикладі компілятор лише вирішив, куди буде виділено масив, зберігаючи цю інформацію у виконуваному файлі.
У випадку ініціалізованих значень компілятор також вводить початкове значення масиву у виконуваний файл і додає код, який повідомляє завантажувачу програми, що після розподілу масиву при запуску програми масив повинен бути заповнений цими значеннями.

Ось два приклади збірки, згенерованої компілятором (GCC4.8.1 з ціллю x86):

Код C ++:

int a[4];
int b[] = { 1 , 2 , 3 , 4 };

int main()
{}

Вихідна збірка:

a:
    .zero   16
b:
    .long   1
    .long   2
    .long   3
    .long   4
main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, %eax
    popq    %rbp
    ret

Як бачите, значення безпосередньо вводяться в збірку. У масиві aкомпілятор генерує нульову ініціалізацію 16 байтів, оскільки Стандарт говорить, що статичні збережені речі повинні бути ініціалізовані до нуля за замовчуванням:

8.5.9 (Ініціалізатори) [Примітка]:
Кожен об'єкт статичної тривалості зберігання нульово ініціалізується при запуску програми до того, як відбудеться будь-яка інша ініціалізація. У деяких випадках додаткова ініціалізація проводиться пізніше.

Я завжди пропоную людям розібрати свій код, щоб побачити, що компілятор насправді робить з кодом C ++. Це стосується класів / тривалості зберігання (як це питання) до розширених оптимізацій компілятора. Ви можете доручити своєму компілятору створити збірку, але є чудові інструменти, щоб зробити це в Інтернеті дружньо. Мій улюблений - GCC Explorer .


2
Дякую. Це багато що уточнює. Таким чином, компілятор виводить щось еквівалентне "резервувати пам'ять від 0xABC до 0xXYZ для масиву змінної [] і т.д." а потім завантажувач використовує це, щоб дійсно виділити його безпосередньо перед запуском програми?
Талха сказала

1
@TalhaSayed точно. Дивіться правку, щоб переглянути приклад
Manu343726

2
@Secko Я спростив речі. Єдина згадка про програму працює через віртуальну пам’ять, але оскільки питання не у віртуальній пам'яті, я не поширював цю тему. Я лише вказував, що компілятор може робити припущення щодо адрес пам'яті під час компіляції, завдяки віртуальній пам'яті.
Manu343726

2
@Secko так. ммм "генерований" - це кращий термін, я думаю.
Manu343726

2
"Її виділено в статичній маморі області простору пам'яті процесу" Читання, яке виділяло деякі статичні області молочної залози в моєму просторі пам'яті процесу.
Radiodef

27

Пам'ять, виділена під час компіляції, просто означає, що не буде подальше розподіл під час виконання - ніяких викликів malloc, нових чи інших динамічних методів розподілу. У вас буде фіксований об'єм пам'яті, навіть якщо вам не потрібно весь цей час.

Чи не розподіл пам’яті за визначенням не є концепцією виконання?

Пам'ять не використовується до часу запуску, але безпосередньо перед виконанням запуску її розподілу обробляється системою.

Якщо я зроблю статично виділену змінну розміром 1 КБ у своєму коді C / C ++, чи збільшить це розмір виконуваного файлу на ту ж суму?

Просто оголошення статики не збільшить розмір виконуваного файлу більше ніж на кілька байт. Оголосивши його початковим значенням, яке не є нульовим, буде (щоб утримувати це початкове значення). Швидше за все, лінкер просто додає цю суму в 1 КБ до вимоги пам'яті, яку завантажувач системи створює для вас безпосередньо перед виконанням.


1
якщо я напишу static int i[4] = {2 , 3 , 5 ,5 }, чи збільшиться він на розмір виконавчого файлу на 16 байт. Ви сказали: "Просто оголошення статики не збільшить розмір вашого виконуваного файлу більше ніж на кілька байт. Оголосити його з початковим значенням, яке не є нульовим," оголосити його початковим значенням буде те, що це означає.
Сурай Джайн

Ваш виконуваний файл має дві області для статичних даних - одну для неініціалізованої статики та одну для ініціалізованої статики. Неініціалізована область насправді є лише показником розміру; коли ваша програма запускається, цей розмір використовується для збільшення статичної області зберігання, але сама програма не повинна містити нічого більше, ніж кількість неініціалізованих даних. Для ініціалізованої статики ваша програма повинна містити не тільки розмір (кожного) статичного, але і те, до чого воно стає ініціалізованим. Таким чином, у вашому прикладі у вашій програмі буде 2, 3, 5 і 5.
Мая

Це реалізація визначена щодо того, куди вона розміщується / як вона виділяється, але я не впевнений, що я розумію, що потрібно знати.
ма

23

Пам'ять, виділена під час компіляції, означає, що при завантаженні програми деяка частина пам'яті буде негайно виділена, а розмір та (відносне) положення цього розподілу визначаються під час компіляції.

char a[32];
char b;
char c;

Ці 3 змінні "розподіляються під час компіляції", це означає, що компілятор обчислює їх розмір (який фіксується) під час компіляції. Змінна aбуде зміщена в пам'яті, скажімо, вказуючи на адресу 0, bбуде вказувати на адреси 33 та c34 (припустимо, що оптимізація вирівнювання не буде). Отже, виділення 1 Кб статичних даних не збільшить розмір вашого коду , оскільки він просто змінить зміщення всередині нього. Фактичний простір буде розподілений під час завантаження .

Реальний розподіл пам’яті завжди відбувається під час виконання, тому що ядро ​​має відслідковувати його та оновлювати свої внутрішні структури даних (скільки пам'яті виділяється на кожен процес, сторінки тощо). Різниця полягає в тому, що компілятор вже знає розмір кожної інформації, яку ви збираєтеся використовувати, і вона розподіляється, як тільки ваша програма виконується.

Пам'ятайте також, що ми говоримо про відносні адреси . Реальна адреса, де буде розміщена змінна, буде різною. Під час завантаження ядро ​​збереже деяку пам’ять для процесу, дозволить сказати адресу x, і всі жорстко закодовані адреси, що містяться у виконуваному файлі, будуть нарощені xбайтами, так що змінна aу прикладі буде знаходитись за адресою x, b за адресою x+33та так далі.


17

Додавання змінних у стек, що займає N байтів, не обов'язково збільшує розмір біна на N байт. Фактично це додасть лише кілька байт більшості часу.
Давайте почнемо з прикладом того , як додавання в 1000 символів в ваш код буде збільшувати розмір БІН в лінійному вигляді.

Якщо 1k - це рядок, має тисячу символів, що оголошується так

const char *c_string = "Here goes a thousand chars...999";//implicit \0 at end

і ви тоді повинні були vim your_compiled_bin, ви насправді змогли б десь побачити цей рядок у відро. У такому випадку, так: виконуваний файл буде на 1 к більше, тому що він містить рядок повністю.
Якщо ви виділите масив ints, chars або longs на стек і призначите його в циклі, щось уздовж цих рядків

int big_arr[1000];
for (int i=0;i<1000;++i) big_arr[i] = some_computation_func(i);

то ні: вона не збільшить кошик ... 1000*sizeof(int)
Розподіл на час компіляції означає те, що ви зараз зрозуміли, що це означає (виходячи з ваших коментарів): складений контейнер містить інформацію, яку система потребує, щоб знати, скільки пам'яті яка функція / блок знадобиться при його виконанні, а також інформація про розмір стека, який вимагає ваша програма. Це те, що система виділить, коли вона виконує ваш бін, і ваша програма перетвориться на процес (ну, виконання вашого біна - це процес, який ... ну, ви отримуєте те, що я говорю).
Звичайно, я не малюю тут повного зображення: У кошику міститься інформація про те, наскільки великий стек буде потрібен насправді. Виходячи з цієї інформації (серед іншого), система резервує шматок пам'яті, який називається стеком, що програма отримує своєрідне вільне управління. Пам'ять про стек все ще виділяється системою, коли ініціюється процес (результат виконання вашого кошика). Потім процес управляє пам'яттю стека для вас. Коли функція або цикл (будь-який тип блоку) викликається / виконується, змінні, локальні для цього блоку, висуваються в стек, і вони видаляються (пам'ять стека "звільняється", так би мовити), щоб використовуватись іншими функції / блоки. Так декларуючиint some_array[100]буде додано лише кілька байтів додаткової інформації до кошика, що повідомляє системі, що функція X вимагатиме 100*sizeof(int)додаткового місця в бухгалтерському обліку.


Дуже дякую. Ще одне питання: чи місцеві змінні для функцій також розподіляються однаково під час компіляції?
Талха сказала

@TalhaSayed: Так, це я мав на увазі, коли сказав: "Інформація, яку система потребує, щоб знати, скільки пам'яті потрібна функція / блок". Щойно ви викликаєте функцію, система виділить необхідну пам'ять для цієї функції. Щойно функція повернеться, ця пам'ять знову буде звільнена.
Elias Van Ootegem

Щодо коментарів у вашому коді С: Це насправді / не обов'язково те, що відбувається. Наприклад, рядок, швидше за все, буде виділено лише один раз, під час компіляції. Таким чином, він ніколи не "звільняється" (також я думаю, що термінологія, як правило, використовується лише тоді, коли ви динамічно виділяєте щось), iне є "звільненим" або тим чи іншим. Якби iвони мешкали в пам'яті, її просто натиснули б на стек, щось не звільнене в цьому сенсі цього слова, ігноруючи це iабо cбуде утримуватися в регістрах весь час. Звичайно, все це залежить від компілятора, а значить, не все це чорно-біле.
phant0m

@ phant0m: Я ніколи не казав, що рядок виділяється на стеці, тільки вказівник теж був би, сам рядок міститиметься в пам'яті, лише для читання. Я знаю, що пам'ять, пов’язана з локальними змінними, не звільняється в сенсі free()викликів, але пам'ять стека, яку вони використовували, вільна для використання іншими функціями, як тільки функція, яку я перераховував, повертається. Я видалив код, оскільки він може бентежити деяких
Еліас Ван Оотегем,

А, бачу. У такому випадку сприйміть мій коментар, щоб він означав, що "я був бентежений вашою формулюванням"
phant0m

16

На багатьох платформах всі глобальні або статичні виділення в межах кожного модуля компілятором будуть консолідовані в три або менші консолідовані виділення (одне для неініціалізованих даних (часто називається "bss"), інше для ініціалізованих даних, що записуються (часто називаються "дані" ), і один для постійних даних ("const")), і всі глобальні або статичні виділення кожного типу в рамках програми консолідуються лінкером в один глобальний для кожного типу. Наприклад, якщо припустимо, що intце чотири байти, модуль має такі лише свої статичні виділення:

int a;
const int b[6] = {1,2,3,4,5,6};
char c[200];
const int d = 23;
int e[4] = {1,2,3,4};
int f;

це скаже линкеру, що йому потрібно 208 байт для bss, 16 байт для "даних" і 28 байт для "const". Далі, будь-яке посилання на змінну буде замінено селектором площі та зміщенням, тому a, b, c, d та e буде замінено bss + 0, const + 0, bss + 4, const + 24, data +0, або bss + 204 відповідно.

Коли програма пов'язана, всі області bss з усіх модулів об'єднуються разом; аналогічно областям даних та const. Для кожного модуля адреса будь-яких bss-відносних змінних буде збільшена на розмір областей bss усіх попередніх модулів (знову ж таки, з даними та const). Таким чином, коли лінкер виконаний, будь-яка програма матиме один розподіл bss, одне розподілення даних та одне розподілення const.

Коли програма завантажується, одна із чотирьох речей, як правило, відбуватиметься залежно від платформи:

  1. Виконавчий файл буде вказувати, скільки байтів потрібно для кожного виду даних і - для ініціалізованої області даних, де може бути знайдено початковий вміст. Він також буде містити перелік усіх інструкцій, які використовують адресу bss-, data- або const-. Операційна система або завантажувач виділить відповідну кількість місця для кожної області, а потім додадуть вихідну адресу цієї області до кожної інструкції, яка їй потрібна.

  2. Операційна система виділить шматок пам’яті для зберігання всіх трьох видів даних і додасть додатку вказівник на цей фрагмент пам’яті. Будь-який код, який використовує статичні або глобальні дані, буде відмежувати його відносно цього покажчика (у багатьох випадках вказівник буде зберігатися в реєстрі протягом життя програми).

  3. Операційна система спочатку не буде виділяти будь-яку пам'ять додатку, за винятком того, що містить її двійковий код, але перше, що додаток робить, це запитати відповідне виділення в операційній системі, яке воно назавжди зберігатиме в реєстрі.

  4. Операційна система спочатку не виділяє простір для програми, але програма вимагатиме відповідного розподілу при запуску (як зазначено вище). Додаток буде містити перелік інструкцій з адресами, які потрібно оновити, щоб відобразити місце, де було виділено пам'ять (як у першому стилі), але замість того, щоб додаток було зафіксовано завантажувачем ОС, додаток буде містити достатньо коду, щоб виправити себе .

Усі чотири підходи мають переваги та недоліки. Однак у кожному випадку компілятор об'єднає довільну кількість статичних змінних у фіксовану невелику кількість запитів на пам'ять, а лінкер об'єднає всі ці в невелику кількість консолідованих розподілів. Навіть незважаючи на те, що програмі доведеться отримувати шматок пам'яті з операційної системи або завантажувача, саме компілятор і лінкер відповідають за розподіл окремих частин з цього великого фрагмента для всіх окремих змінних, які цього потребують.


13

Суть вашого питання полягає в наступному: "Як пам'ять" виділяється "у складеному файлі? Чи пам'ять не завжди виділяється в оперативній пам'яті з усіма елементами управління віртуальною пам'яттю?

Я думаю, що проблема полягає в тому, що в розподілі пам’яті беруть участь дві різні концепції. В основному, розподіл пам'яті - це процес, за допомогою якого ми кажемо, що "цей елемент даних зберігається в цьому конкретному фрагменті пам'яті". У сучасній комп'ютерній системі це передбачає двоступеневий процес:

  • Деяка система використовується для визначення віртуальної адреси, за якою елемент буде зберігатися
  • Віртуальна адреса відображається на фізичну адресу

Останній процес - це суто час виконання, але перший може бути виконаний під час компіляції, якщо дані мають відомий розмір і потрібна фіксована їх кількість. Ось як це працює:

  • Компілятор бачить вихідний файл, що містить рядок, який виглядає приблизно так:

    int c;
  • Він дає вихід асемблеру, який вказує йому резервувати пам'ять для змінної 'c'. Це може виглядати приблизно так:

    global _c
    section .bss
    _c: resb 4
  • Коли асемблер працює, він зберігає лічильник, який відслідковує зміщення кожного елемента з початку заповнення «сегмента» пам'яті (або «розділу»). Це подібно до частин дуже великої "структури", яка містить усе, що є у всьому файлі, на даний момент йому не виділена фактична пам'ять, і вона може бути де завгодно. Він зазначає в таблиці, яка _cмає певний зсув (скажімо, 510 байт з початку сегмента), а потім збільшує його лічильник на 4, тому наступна така змінна буде на рівні (наприклад) 514 байт. Для будь-якого коду, якому потрібна адреса _c, він просто ставить 510 у вихідний файл і додає примітку, що для виводу потрібна адреса сегмента, який містить _cдодавання до нього пізніше.

  • Лінкер бере всі вихідні файли асемблера та вивчає їх. Він визначає адресу для кожного сегмента, щоб вони не перекривались, і додає необхідні компенсації, щоб інструкції все-таки посилалися на правильні елементи даних. У випадку неініціалізованої пам’яті на зразок тієї, яку займаєc(асемблеру було сказано, що пам'ять буде неініціалізована тим, що компілятор помістив її в сегмент '.bss', що є ім'ям, зарезервованим для неініціалізованої пам'яті), вона містить у своєму виході поле заголовка, яке повідомляє операційну систему скільки потрібно зарезервувати. Він може бути переміщений (і зазвичай є), але зазвичай призначений для ефективнішого завантаження на одну конкретну адресу пам'яті, і ОС намагатиметься завантажити його за цією адресою. На даний момент у нас є досить гарна ідея, що таке віртуальна адреса, якою буде користуватися c.

  • Фізична адреса фактично не буде визначена до запуску програми. Однак, з точки зору програміста, фізична адреса насправді не має значення - ми навіть ніколи не дізнаємося, що це таке, тому що ОС зазвичай не турбує когось сказати, вона може часто змінюватися (навіть під час роботи програми), і Основна мета ОС - це все-таки абстрагувати це.


9

Виконаний файл описує, який простір потрібно виділити для статичних змінних. Цей розподіл виконується системою під час запуску виконуваного файлу. Таким чином, статична змінна розміром 1 КБ не збільшить розмір виконуваного файлу за допомогою 1 кБ:

static char[1024];

Якщо ви, звичайно, не вказали ініціалізатор:

static char[1024] = { 1, 2, 3, 4, ... };

Отже, крім 'машинної мови' (тобто інструкцій з процесора), виконуваний файл містить опис необхідної компонування пам'яті.


5

Пам'ять можна виділити багатьма способами:

  • у купі додатків (вся програма виділяється для вашої програми ОС, коли програма запускається)
  • в купі операційної системи (так що ви можете захоплювати все більше і більше)
  • в купі, що контролюється сміттям (те саме, що і вище)
  • на стеці (щоб ви могли отримати переповнення стека)
  • зарезервовано у сегменті коду / даних вашого двійкового файлу (виконуваний файл)
  • у віддаленому місці (файл, мережа - і ви отримуєте ручку, а не вказівник на цю пам'ять)

Тепер ваше питання полягає в тому, що таке "пам'ять, виділена під час компіляції". Безумовно, це лише неправильно фразована приказка, яка повинна посилатися або на розподіл двійкових сегментів або на розподіл стека, або в деяких випадках навіть на розподіл купи, але в цьому випадку розподіл приховано від очей програміста невидимим викликом конструктора. Або, мабуть, людина, яка сказала, що просто хотіла сказати, що пам'ять не виділяється на купі, але не знала про розподіл стека чи сегментів. (Або не хотіла вникати в цю деталь).

Але в більшості випадків людина просто хоче сказати, що обсяг пам'яті, що виділяється, відомий під час компіляції .

Бінарний розмір зміниться лише тоді, коли пам’ять зарезервована у коді чи сегменті даних вашої програми.


1
Ця відповідь є заплутаною (або заплутаною) тим, що вона говорить про "купу додатків", "купу операційної системи" та "купу GC", як ніби це все значущі поняття. Я роблю висновок, що під №1 ви намагалися сказати, що деякі мови програмування можуть (гіпотетично) використовувати схему "розподілу куч", яка виділяє пам'ять із буфера фіксованого розміру в розділі .data, але це здається нереально реалістичним, щоб бути шкідливим на розуміння ОП. Щодо №2 та №3, присутність GC насправді нічого не змінює. І повторно №5, ви пропустили порівняно МНОГО більш важливе розмежування між .dataта .bss.
Квомплусон

4

Ти правий. Пам'ять фактично розподіляється (підказка) під час завантаження, тобто коли виконуваний файл вноситься у (віртуальну) пам'ять. Пам'ять також може бути ініціалізована в цей момент. Компілятор просто створює карту пам'яті. [До речі, простір для стека та купи також виділяється під час завантаження!]


2

Я думаю, вам потрібно трохи відступити. Пам'ять, виділена під час компіляції .... Що це може означати? Чи може це означати, що пам’ять на ще не виготовлених мікросхемах для комп’ютерів, які ще не були розроблені, якимось чином зарезервована? Ні. Ні, подорож у часі, немає компіляторів, які могли б маніпулювати Всесвітом.

Отже, це має означати, що компілятор генерує вказівки, щоб розподілити цю пам'ять якось під час виконання. Але якщо подивитися на це під прямим кутом, компілятор генерує всі інструкції, тож у чому може бути різниця. Різниця полягає в тому, що компілятор приймає рішення, а під час виконання ваш код не може змінювати чи змінювати свої рішення. Якщо він вирішив, що потрібно 50 байт під час компіляції, під час виконання, ви не можете прийняти рішення про виділення 60 - це рішення вже прийняте.


Мені подобаються відповіді, які використовують метод Сократа, але я все-таки заперечував вас за помилковий висновок про те, що "компілятор генерує вказівки розподілити цю пам'ять якось під час виконання". Ознайомтеся з голосовою відповіддю, щоб побачити, як компілятор може "розподіляти пам'ять", не створюючи жодних "інструкцій" для виконання. (Зауважте, що "вказівки" в мові збірки мають специфічне значення, тобто виконувані коди. Можливо, ви використовували слово розмовно, щоб означати щось на зразок "рецепт", але в цьому контексті це просто заплутає ОП. )
Квомплусон

1
@Quuxplusone: Я прочитав (і схвалив) цю відповідь. І ні, моя відповідь конкретно не стосується питання ініціалізованих змінних. Він також не стосується коду, що самовиправляється. Хоча ця відповідь відмінна, вона не стосувалася того, що я вважаю важливим питанням - укладання речей у контекст. Звідси моя відповідь, яка, сподіваюсь, допоможе ОП (та іншим) зупинитися і подумати про те, що відбувається чи що може відбуватися, коли у них будуть проблеми, які вони не розуміють.
jmoreno

@Quuxplusone: Вибачте, якщо я тут висловлюю неправдиві звинувачення, але я вважаю, що ви були одним із людей, які також відповіли моєю відповіддю. Якщо так, чи не заперечуєте ви, зазначивши, яка частина моєї відповіді була основною причиною цього, і ви також хотіли б перевірити мою редакцію? Я знаю, що я пропустив декілька бітів про справжню внутрішню інформацію про те, як керується пам'яттю стека, тому я тепер додав трохи про те, що зараз я не на 100% точний, щоб відповісти :)
Elias Van Ootegem

@jmoreno Точка, про яку ви говорили про "Чи може це означати, що пам'ять на мікросхемах, які ще не були виготовлені, для комп'ютерів, які ще не були розроблені, якимось чином зарезервована? Ні." саме те хибне значення, яке має на увазі слово "виділення", яке мене з самого початку бентежило. Мені подобається ця відповідь, оскільки вона стосується саме тієї проблеми, яку я намагався вказати. Жодна з відповідей тут дійсно не торкнулася саме цього моменту. Дякую.
Талха сказала

2

Якщо ви вивчите програмування складання, ви побачите, що вам потрібно вирізати сегменти для даних, стека та коду тощо. Сегмент даних - це місце, де живуть ваші рядки та числа. У сегменті коду живе ваш код. Ці сегменти вбудовані у виконувану програму. Звичайно, важливий також розмір стека ... ви не хочете переповнювати стек !

Отже, якщо ваш сегмент даних становить 500 байт, програма має 500 байт. Якщо змінити сегмент даних на 1500 байт, розмір програми буде на 1000 байт більшим. Дані збираються у фактичну програму.

Це те, що відбувається під час складання мов вищого рівня. Фактична область даних виділяється, коли вона компілюється у виконувану програму, збільшуючи розмір програми. Програма також може запитати пам'ять на льоту, а це динамічна пам'ять. Ви можете попросити пам'ять з оперативної пам’яті, і процесор передасть її вам у користування, ви можете відпустити її, і ваш сміттєзбірник поверне її в процесор. Його навіть можна замінити на жорсткий диск, якщо потрібно, хорошим менеджером пам'яті. Ці функції - це те, що надають вам мови високого рівня.


2

Я б хотів пояснити ці поняття за допомогою кількох діаграм.

Це правда, що пам’ять не може бути виділена під час компіляції. Але, що відбувається насправді під час компіляції.

Ось приходить пояснення. Скажімо, наприклад, що програма має чотири змінні x, y, z і k. Тепер, під час компіляції, він просто складає карту пам'яті, де визначається розташування цих змінних відносно один одного. Ця діаграма буде ілюструвати це краще.

Тепер уявіть, що жодна програма не працює в пам'яті. Це я показую великим порожнім прямокутником.

порожнє поле

Далі виконується перший екземпляр цієї програми. Ви можете візуалізувати його наступним чином. Це час, коли фактично виділяється пам'ять.

перша інстанція

Коли працює другий екземпляр цієї програми, пам'ять виглядатиме так.

друга інстанція

І третє ..

третя інстанція

І так далі.

Я сподіваюся, що ця візуалізація добре пояснює цю концепцію.


2
Якби ці діаграми показали різницю між статичною та динамічною пам'яттю, вони були б кориснішими IMHO.
Bartek Banachewicz

Цього я навмисно уникав, щоб все було просто. Моя увага - пояснити цю фундацію чітко, без особливих технічних проблем. Наскільки це призначено для статичної змінної. Цей пункт був добре встановлений попередніми відповідями. Отже, я пропустив це.
user3258051

1
Е, ця концепція не є особливо складною, тому я не розумію, чому зробити її простішою, ніж це має бути, але оскільки це означає лише як додаткову відповідь, добре.
Bartek Banachewicz

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.