单独声明和初始化的变量如何在内存中工作?

时间:2017-07-17 16:00:02

标签: c arrays memory

我对计算机内存的工作有疑问。我试图自己得到答案,但我无法弄清楚它是如何工作的。因此,想象一个人声明指向未来字符串的指针的情况;但是,初始化会稍晚:

char *str; 

之后,他或她想宣布另一个变量。

char her;  

在此之后,两个变量都被初始化,它们的地址和值被打印到STDOUT。整个程序看起来像这样:

int     main(void)
{
    char *str;
    char her;

    her = 'Y';
    str = "HelloMuraMana";
    printf("%p\n", (void *)&str);
    printf("%p\n", (void *)&(str[1]));
    printf("%p\n", (void *)&(str[2]));
    printf("%p\n", (void *)&(str[3]));
    printf("%p\n", (void *)&(str[4]));
    printf("%p\n", (void *)&her);
    return (0);
}    

现在,我的问题是:计算机如何为两个变量分配内存(尤其是字符串字符)。我想添加一张图片,我的macOS机器显示了我的结果:

CLICK HERE to see the results

编辑:
我特别感兴趣的是记忆在这里工作的方式。另请注意,str [0]有一个地址,str 1,str [2],str [3]和str [4]有其他,与第一个元素地址不连续。

3 个答案:

答案 0 :(得分:0)

从概念上讲,当程序加载到内存中时,它有3个区域(段):

  • 代码段:程序文本存储在此处(它是一个只读区域)
  • 数据段:包含具有预定义值且可以修改的任何全局变量或静态变量
  • 堆栈段:这里加载了调用它们的函数。每个函数调用在堆栈上推送的值集(堆栈帧),其中包含函数和局部变量的返回地址。

在您的情况下,char her变量是main()函数中的局部变量,该变量使用值Y进行初始化。因此,它存储在堆栈中并可以修改。

char *str变量是指向"HelloMuraMana\0"地址的指针,export default function users 是位于代码段中的常量。作为只读区域,您无法修改其内容。

答案 1 :(得分:0)

就像我在评论中所说的那样,内存中具体化内容的细节会因平台而异。但是,这是一个非常高级的视图,应该给出一些有用的东西。

首先,在C编程语言的上下文中,对象可以具有多个存储持续时间之一:staticautomaticallocated和{ {1}}。程序启动时会分配具有thread local存储持续时间的对象,并在程序退出时释放。具有static存储持续时间的对象在进入其封闭范围(函数或块)时分配,并在该范围退出时立即释放。存储时间automatic的对象通过调用allocated进行分配,并通过调用malloc/calloc/realloc释放。我不打算进入free,因为它与这个讨论并不相关。

当你的程序被加载到内存中时,它的布局是这样的(假设是x86或类似的):

thread local

具体图片取决于您的系统。请注意,这是虚拟地址空间中的内容,而不是物理内存。

当您的程序运行时, +------------------------+ high address | Command line arguments | | and environment vars | +------------------------+ | stack | <-- str and her live here, but | - - - - - - - - - - - | only for the duration of main() | | | | V | | | | ^ | | | | | - - - - - - - - - - - | | heap | +------------------------+ | global and read- | <-- "HelloMuraMana" lives here for | only data | duration of the program +------------------------+ | program text | low address | (machine code) | +------------------------+ 变量的存储(函数参数和函数本地变量或autoher之类的块)将从标记为{{1}的区域中分配}。 str个对象的存储空间是从标有stack的区域分配的。

allocated个对象的存储以及字符串文字的存储(如heap)来自不同的段;在上图中,它将是标记为static的细分。根据您的系统,字符串文字可能存储在只读段(例如"HelloMuraMana"global and read-only data)中,或者它们可能位于可写段中。字符串文字假定是不可变的(因此称为“文字”),因此尝试修改字符串文字将导致未定义的行为。

在上面的布局中,全局数据对象的地址低于堆栈或堆对象,这些对象由输出显示。当您输入.rodata时,变量.rdata将从堆栈中分配;它的值是字符串文字的地址,它是在程序首次启动时从全局数据段分配的。

答案 2 :(得分:-1)

你应该选择一个稍微简单的例子:

int     main(void)
{
    char *str;
    char her;

    her = 'Y';
    str = "HelloMuraMana";
    puts(str);
    putchar(her);
    return (0);
}    

compile it into assembly并检查:

.LC0:
        .string "HelloMuraMana"
main:
        pushq   %rbp                     
        movq    %rsp, %rbp               //frame setup
        subq    $16, %rsp                //allocate 2*8bit words for the 2 variables
        movb    $89, -1(%rbp)            //initialize her
        movq    $.LC0, -16(%rbp)         //initialize str
        movq    -16(%rbp), %rax          //prepare the argument to puts
        movq    %rax, %rdi
        call    puts                     //self-descriptive
        movsbl  -1(%rbp), %eax           //prepare the argument to putchar
        movl    %eax, %edi
        call    putchar                  //self-descriptive
        movl    $0, %eax                 //prepare the main return value
        leave
        ret

现在,正如您从反汇编中看到的那样,两个堆栈变量由堆栈指针减法(subq $16, %rsp)分配(因为堆栈通常向下增长)。字符串文字字符数组是一个静态生命周期变量,它进入程序加载程序在程序加载时将分配的段。