堆栈段中意外的内存分配

时间:2016-01-31 14:35:25

标签: c stack-memory

我试图看到对于给定的函数,内存的堆栈段上的内存分配将以连续的方式发生。所以,我写了下面的代码,我得到了以下输出。

对于int分配,我看到内存地址按预期发送,但不是字符数组。在内存地址0xbff1599c之后,我希望下一个地址为0xbff159a0,而不是0xbff159a3。此外,由于char是1个字节而我使用的是4个字节,所以在0xbff159a3之后我期待0xbff159a7而不是0xbff159a8

如果删除char部分但是我无法获得具有字符数组的预期内存位置,则所有内存位置都按预期方式进行。

我的基本假设是在堆栈段上,内存将始终是连续的。我希望这没错。

#include <stdio.h>

int main(void)
{
    int x = 10;
    printf("Value of x is %d\n", x);
    printf("Address of x is %p\n", &x);
    printf("Dereferencing address of x gives %d\n", *(&x));
    printf("\n");

    int y = 20;
    printf("Value of y is %d\n", y);
    printf("Address of y is %p\n", &y);
    printf("Dereferencing address of y gives %d\n", *(&y));
    printf("\n");

    char str[] = "abcd";
    printf("Value of str is %s\n", str);
    printf("Address of str is %p\n", &str);
    printf("Dereferencing address of str gives %s\n", *(&str));
    printf("\n");

    int z = 30;
    printf("Value of z is %d\n", z);
    printf("Address of z is %p\n", &z);
    printf("Dereferencing address of z gives %d\n", *(&z));
}

输出

Value of x is 10
Address of x is 0xbff159ac
Dereferencing address of x gives 10

Value of y is 20
Address of y is 0xbff159a8
Dereferencing address of y gives 20

Value of str is abcd
Address of str is 0xbff159a3
Dereferencing address of str gives abcd

Value of z is 30
Address of z is 0xbff1599c
Dereferencing address of z gives 30

3 个答案:

答案 0 :(得分:2)

  

此外,由于char是1个字节而我使用4个字节,所以在0xbff159a3之后我期待0xbff159a7而不是0xbff159a8

char占用1个字节,但str是字符串,并且您没有计算位于字符串末尾的'\0'因此char str[]="abcd"占用5个字节。

答案 1 :(得分:1)

我认为这可能是因为地址与边界对齐(例如8字节边界)?。

分配始终与边界对齐并以块的形式分配 在某些操作系统中您可以使用结构进行检查。例如,     结构A.     {       char a;       char b;       int c;     };

在UNIX / LINUX平台上,struct的大小不会是6个字节。

但它可能因操作系统而异。

类似的东西也适用于其他数据类型。 而且,字符串只指向a中分配的地址 堆如果使用malloc并且分配逻辑可能会有所不同 从OS到OS。以下是Linux框输出 对于同一个程序。

x的值是10 x的地址是0x7ffffa43a50c 取消引用x的地址为10

y的值是20 y的地址是0x7ffffa43a508 取消引用y的地址给出20

str的值是abcd str的地址是0x7ffffa43a500 取消引用str的地址给出了abcd

z的值是30 z的地址是0x7ffffa43a4fc 取消引用z的地址给出30

答案 2 :(得分:1)

来自@ameyCU和@Umamahesh的答案都很好,但没有一个是自给自足的,所以我正在写我的答案并添加更多信息,以便进一步访问的人可以获得最大的知识。

由于名为 Data structure alignment 的概念,我得到了这个结果。根据这一点,计算机将始终尝试以块的方式分配内存(无论是在堆段还是堆栈段或数据段中,在我的情况下是堆栈段),以便它可以读写快。

当现代计算机读取或写入存储器地址时,它将以字大小的块(例如,32位系统上的4字节块)或更大的块来执行此操作。数据对齐意味着将数据放入存储器地址等于字大小的某个倍数,这会因CPU处理内存的方式而提高系统性能。

在32位架构上,计算机字大小为4个字节,因此计算机将始终尝试分配具有4的倍数的地址的内存,以便它可以以4个字节的块快速读写。当字节数较少时,计算机会在开始或结束时填充一些空字节。

就我而言,假设我使用char str[] = "abc";然后包含EOL字符'\0'我需要4个字节,因此不会有填充。但是当我char str[] = "abcd";然后包含EOL字符'\0'时,我需要5个字节,现在计算机想要在4个块中分配,所以它将添加3个字节的填充(在开始或结束)因此完整的char数组将在内存中超过8个字节。

由于intlong内存要求已经是4的倍数,所以没有问题,使charshort变得棘手多个4 。这解释了我报告的内容 - “如果我删除了char部分但是我无法通过字符数组获得预期的内存位置,那么所有内存位置都会按预期进行。

经验法则是指如果您的内存要求不是4的倍数(例如,1 {1}},short数组大小为2)那么额外的填充将添加然后进行内存分配,以便计算机可以快速读写。

<小时/> 以下是this answer的摘录,它解释了数据结构的对齐。

假设你有结构。

char

如果没有对齐,它将在内存中布局(假设采用32位架构):

struct S {
    short a;
    int b;
    char c, d;
};

问题在于,在某些CPU架构中,从内存加载4字节整数的指令仅适用于字边界。因此,您的程序必须使用单独的指令获取b的每一半。

但如果记忆的布局如下:

 0 1 2 3 4 5 6 7
|a|a|b|b|b|b|c|d|  bytes
|       |       |  words

然后访问b变得简单明了。 (缺点是需要更多内存,因为填充字节。)