2016-06-11 2 views
4

Я делаю несколько примеров размещения стека и кучи на Ubuntu 14.04 VM (Linux 3.13.0-55-generic i686), и я смущен адресами памяти для распределений кучи.В чем разница в адресах этих стеков и кучей памяти?

Приведенный ниже код кода выделяет три 32-разрядных беззнаковых ints в стеке и три распределения в куче уменьшающихся размеров, 32 бита, 16 бит и, наконец, 8 бит.

На выходе ниже мы видим, что адреса памяти для трех 32-битных ints в стеке разделены на 4 бита. uint32_t i находится на 0xbffd4818, а 4 адреса позже на 0xbffd481c - uint32_t j. Таким образом, мы можем видеть, что каждый отдельный байт памяти адресуем, и поэтому каждый 4-байтовый блок памяти имеет 4 адреса памяти.

Глядя на распределения кучи, хотя мы видим, что uint32_t i_ptr указывает на 0x99ae008, а malloc запросил 4 байта пространства, поэтому я ожидал, что uint16_t j_ptr начнется с 0x99ae00c, но начнется с 0x99ae018. Третье распределение кучи для uint8_t k_ptr запускает 16 байт после uint16_t i_ptr, который также запускает 16 байтов после uint32_t i_ptr.

  1. Является ли это стандартным настройкой ОС по умолчанию, что каждое распределение кучи составляет 16 байт?
  2. Почему это происходит независимо от размера, который я передал , чтобы malloc?
  3. Как мы можем поместить 4 байта информации между 0x99ae008 и 0x99ae018?

C Источник:

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

int main() { 

    register uint32_t ebp asm ("ebp"); 
    printf("0x%x\n", ebp); 

    register uint32_t esp asm ("esp"); 
    printf("0x%x\n", esp); 

    uint32_t i; 
    printf("%p\n", &i); 

    uint32_t j; 
    printf("%p\n", &j); 

    uint32_t k; 
    printf("%p\n", &k); 

    uint32_t *i_ptr = malloc(4); 
    printf("%p\n", i_ptr); 

    uint16_t *j_ptr = malloc(2); 
    printf("%p\n", j_ptr); 

    uint8_t *k_ptr = malloc(1); 
    printf("%p\n", k_ptr); 

    free(i_ptr); 
    free(j_ptr); 
    free(k_ptr); 

    return 0; 

} 

выход CLI:

$ gcc -o heap2 heap2.c 
$ ./heap2 
0xbffd4838 // EBP 
0xbffd4800 // ESP 
0xbffd4818 // uint32_t i 
0xbffd481c // uint32_t j 
0xbffd4820 // uint32_t k 
0x99ae008 // uint32_t i_ptr 
0x99ae018 // uint16_t j_ptr 
0x99ae028 // uint8_t k_ptr 

ответ

2

таНос возвращает указатель типа void *, который может быть преобразован в указатель любого другого типа. Таким образом, malloc обеспечивает выравнивание, удовлетворяющее требованиям любого типа.

Обычно malloc возвращает адрес, выравниваемый абзацем (в большинстве систем он равен 16 байтам). И кроме того, malloc выделяет экстенты, которые также имеют минимальный размер абзаца. Так что если вы напишете, например,

char *p = malloc(1); 

Тогда на самом деле malloc резервирует в размере 16 байт.

+0

Это объясняет, почему, когда я называю 'malloc (1)' или 'malloc (2)', указатель всегда разделен на 16 бит, как насчет моего вызова 'malloc (4)'? j_ptr только 16 бит в памяти после 32-разрядного распределения (i_ptr)? – jwbensley

+1

malloc не просто выделяет память для использования пользовательским кодом, есть также память, выделенная для поддержки кучи, например: так что free() знает, что делать. – Jasen

+1

@jwbensley Я вижу, что каждая степень выделенной памяти отличается от 16 байт: 0x99ae008 // uint32_t i_ptr 0x99ae018 // uint16_t j_ptr 0x99ae028 –