代码之家  ›  专栏  ›  技术社区  ›  hagrawal7777

堆栈段中的意外内存分配

  •  -2
  • hagrawal7777  · 技术社区  · 9 年前

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

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

    如果我删除了字符部分,所有的内存位置都会像预期的那样出现,但我无法使用字符数组获得预期的内存位置。

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

    #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 回复  |  直到 9 年前
        1
  •  2
  •   ameyCU    9 年前

    另外,由于char是1字节,我使用的是4字节,所以在0xbff159a3之后,我期望的是0xbf159a7,而不是0xbff59a8

    char 占用 1 字节,但 str 是字符串,您没有计数 '\0' 其位于串的末端, char str[]="abcd" 占用 5 字节。

        2
  •  1
  •   Umamahesh P    9 年前

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

    分配始终与边界对齐,并按块分配 在某些操作系统中。您可以使用结构进行检查。例如 结构A { 字符a; 字符b; 整数c; };

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

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

    类似的情况也适用于其他数据类型。 此外,字符串只指向在 堆,如果使用malloc,分配逻辑可能会有所不同 从操作系统到操作系统。以下是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

        3
  •  1
  •   Community CDub    8 年前

    @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个字节的填充( 开始或结束 )因此完整的字符数组将在存储器中跨越8个字节。

    自从 int , long 内存需求已经是4的倍数,因此没有问题 它变得很棘手 char short 不是4的倍数 。这解释了我报告的事情——” 如果我删除了字符部分,所有的内存位置都会像预期的那样出现,但我无法使用字符数组获得预期的内存位置。 "

    经验法则 如果你的内存需求不是4的倍数(例如1 短的 , 烧焦 大小为2的数组),然后将添加额外的填充,然后进行内存分配,这样计算机可以快速读写。


    以下是摘自 this answer 这解释了数据结构对齐。

    假设你有这个结构。

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

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

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

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

    但如果记忆被安排为:

     0 1 2 3 4 5 6 7 8 9 A B
    |a|a| | |b|b|b|b|c|d| | |
    |       |       |       |
    

    然后访问b变得简单。(缺点是由于填充字节,需要更多的内存。)