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

Я следую книге "C Primer Plus" и сталкиваюсь с проблемой, чтобы понять области памяти. В книге говорится:

Как правило, программа использует разные области памяти для статических объектов, автоматических объектов и динамически распределенных объектов. Этот пример показан в листинге 12.15.

// where.c -- where the memory?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int static_store = 30;
const char * pcg = "String Literal";
int main(void)
{
    int auto_store = 40;
    char auto_string[] = "Auto char Array";
    int *pi;
    char *pcl;

    pi = (int *) malloc(sizeof(int));
    *pi = 35;
    pcl = (char *) malloc(strlen("Dynamic String") + 1);
    strcpy(pcl, "Dynamic String");

    printf("static_store: %d at %p\n", static_store, &static_store);
    printf("  auto_store: %d at %p\n", auto_store, &auto_store);
    printf("         *pi: %d at %p\n", *pi, pi);
    printf("  %s at %p\n", pcg, pcg);
    printf(" %s at %p\n", auto_string, auto_string);
    printf("  %s at %p\n", pcl, pcl);
    printf("   %s at %p\n", "Quoted String", "Quoted String");
    free(pi);
    free(pcl);

    return 0;
}

Запустите код и получите:

static_store: 30 at 0x10a621040
  auto_store: 40 at 0x7ffee55df768
         *pi: 35 at 0x7fbf1d402ac0
  String Literal at 0x10a620f00
 Auto char Array at 0x7ffee55df770
  Dynamic String at 0x7fbf1d402ad0
   Quoted String at 0x10a620f9b

заключение книги:

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

Я мог понять, что они имеют другой адрес. Как я могу заверить, что они из разных регионов?

Ответ 1

У разных регионов очень разные адреса. Если бы они находились в одном регионе, у них были бы похожие адреса. Лучший пример, где мы выделяем 2 объекта в каждом регионе:

#include <stdio.h>
#include <stdlib.h>

int main (void)
{
  int stack1;
  int stack2;
  static int bss1;
  static int bss2;
  static int data1=1;
  static int data2=1;
  int* heap1 = malloc(1);
  int* heap2 = malloc(1);  
  char* rodata1 = "hello";
  char* rodata2 = "world";

  printf(".stack\t%p %p\n",  &stack1,  &stack2);
  printf(".bss\t%p %p\n",    &bss1,    &bss2);
  printf(".data\t%p %p\n",   &data1,   &data2);
  printf(".heap\t%p %p\n",   heap1,    heap2);
  printf(".rodata\t%p %p\n", rodata1,  rodata2);

  free(heap1);
  free(heap2);
}

Вывод (например):

.stack  000000000022FE2C 000000000022FE28
.bss    0000000000407030 0000000000407034
.data   0000000000403010 0000000000403014
.heap   0000000000477C50 0000000000477C70
.rodata 0000000000404000 0000000000404006

Как вы можете видеть, две переменные в одном и том же сегменте имеют почти одинаковые адреса, единственное отличие состоит в размере объектов (и, возможно, некотором пространстве для выравнивания). Хотя у них очень разные адреса по сравнению с переменными в других сегментах.

Ответ 2

В стандарте C указано, что объект может иметь одну из 4 разных времен хранения. Это:

  • статический
  • автоматический
  • выделено
  • нить

В приведенном выше коде рассматриваются первые три из них.

Статический объект объявляется либо в области файла, либо в локальной области со static модификатором. Строковые литералы также являются статическими объектами.

Автоматический объект, обычно называемый локальной переменной, объявляется внутри функции или охватывающей области.

Выделенный объект - это объект, память которого возвращается функцией распределения, такой как malloc.

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

Строковые литералы, в частности, являются статическими объектами и обычно помещаются в специальную часть раздела данных, помеченную только для чтения.

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

Поэтому вам не нужно "уверять", что разные типы переменных находятся в разных регионах. Компилятор позаботится об этом для вас в зависимости от того, как вы их определяете.

Ответ 3

Что может помочь немного получить фактические разделы, выделенные для программы, это команда nm, там вы можете увидеть смещение static_store.

static_store: 30 at 0x600b00 ==> 0000000000600b00 D static_store

Смотрите это в прямом эфире на coliru: http://coliru.stacked-crooked.com/a/1b45e01f508ec7b7

Обратите внимание на прилагаемую команду nm: gcc main.cpp &&./a.out && nm a.out

Однако вы должны иметь в виду, что вы, как правило, используете систему с MMU, тем самым имея адреса виртуальной памяти, сопоставленные с реальной памятью.

Найдите дополнительную информацию, например, на https://www.embeddedrelated.com/showarticle/900.php

Ответ 4

Я хочу попытаться объяснить это более простым способом.

0x... представляет собой шестнадцатеричную строку, представляющую строку двоичных бит. Вы можете думать о нем как о количестве, но сокращении, потому что вам не нужно знать число, просто это относительное значение для других одинаково кодированных чисел. Таким образом, это означает, что "адресное значение" на самом деле является просто числом.

Зачем использовать номера для представления мест памяти? Поскольку для всех целей и задач память представляет собой просто большой массив байтов, значения которых могут считываться по индексу. C логически (не физически) делит этот массив памяти на разные разделы для эффективного хранения. Таким образом, чем ближе 2 адреса расположены в памяти, тем ближе они находятся вместе в представлении массива байтов.

Диапазон адресов, доступный для любого приложения, определяется во время выполнения и на самом деле не является частью определенных пространств памяти. Поэтому, честно говоря, нет причин знать, что определенные предметы находятся в определенной области памяти. Просто, что статистически неправдоподобно, чтобы 2 объекта, находящиеся рядом в памяти, находились в разных регионах.