这些堆栈和堆内存地址有什么区别?

What is the difference in these stack and heap memory addresses?

我正在 Ubuntu 14.04 VM (Linux 3.13.0-55-generic i686) 上进行一些示例堆栈和堆分配,我对堆分配的内存地址感到困惑.

下面的 C 代码在堆栈上分配了三个 32 位无符号整数,在堆上分配了三个大小递减的 32 位、16 位和最后 8 位。

在下面的输出中我们可以看到堆栈上三个 32 位整数的内存地址相隔 4 位。 uint32_t i 在 0xbffd4818 后面的 4 个地址在 0xbffd481c 是 uint32_t j。所以我们可以在这里看到内存的每个单独字节都是可寻址的,因此每个 4 字节内存块相隔 4 个内存地址。

查看堆分配,虽然我们可以看到 uint32_t i_ptr 指向 0x99ae008 并且 malloc 请求了 4 个字节的 space,所以我预计 uint16_t j_ptr 从 0x99ae00c 开始,但它从 0x99ae018 开始。 uint8_t k_ptr 的第三次堆分配从 uint16_t i_ptr 之后的 16 个字节开始,它也在 uint32_t i_ptr.[=12= 之后的 16 个字节开始]

  1. 是否只是默认 OS 设置每个堆分配间隔 16 个字节?
  2. 为什么发生这种情况与我传递的尺寸无关 到 malloc?
  3. 如何在0x99ae008和0x99ae018之间容纳4个字节的信息?

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

malloc returns 类型的指针 void * 可以转换为任何其他类型的指针。所以malloc提供了满足任何类型要求的对齐。

通常 malloc returns 一个按段落对齐的地址(在大多数系统中它等于 16 个字节)。此外,malloc 分配的范围也具有最小的段落大小。所以如果你要写例如

char *p = malloc( 1 );

那么实际上 malloc 保留了 16 字节的范围。