内存分配给指针和它们的数据在哪里?

我的问题是如果我有一些功能

void func1(){
    char * s = "hello";
    char * c;
    int b;
    c = (char *) malloc(15);
    strcpy(c,s);
}

我认为s指针是分配在堆栈上的,但是数据“hello”存储的数据在程序的数据段中存在哪里呢? 至于c和b,它们是单元化的,因为'c =一些内存地址'并且它没有一个,但它是如何工作的? 和b也没有内容,所以它不能存储在堆栈上? 然后,当我们用malloc为堆分配内存c时,现在有一些内存地址,这个单元化的c变量如何给出堆上该字符串的第一个字节的地址?


让我们以同样的东西的两个观点来分这个答案,因为标准只会使这个主题的理解变得复杂化,不过它们都是标准。

两部分共同的主题

void func1() {
    char *s = "hello";
    char *c;
    int b;

    c = (char*)malloc(15);
    strcpy(c, s);
}

第一部分:从一个标准的角度来看

根据标准,这个有用的概念称为自动变量持续时间,其中变量的空间在进入给定范围时被自动保留(具有单位值,又名:垃圾!),可以在这样的过程中设置/访问或不设置范围,并且这样的空间被释放以备将来使用。 注意 :在C ++中,这也涉及对象的构建和销毁。

所以,在你的例子中,你有三个自动变量:

  • char *s ,它被初始化为"hello"的地址。
  • char *c ,它保存垃圾,直到它被稍后的赋值初始化为止。
  • int b ,它在整个生命周期中都持有垃圾。
  • 顺便说一句 ,标准没有规定存储如何与功能协同工作。

    第二部分:从现实世界的角度来看

    在任何体面的计算机体系结构中,您都会找到一个称为堆栈的数据结构。 该堆栈的目的是保存可以被自动变量使用和回收的空间,以及用于递归/函数调用所需的某些空间的一些空间,并且可以用作保存临时值(用于优化目的)的地方,如果编译器决定。

    堆栈以PUSH / POP方式工作,即堆栈向下增长。 让我解释一下好一点。 想象一下这样一个空的堆栈:

    [Top of the Stack]
    [Bottom of the Stack]
    

    例如,如果你PUSH一个值为5int ,你会得到:

    [Top of the Stack]
    5
    [Bottom of the Stack]
    

    那么,如果你PUSH -2

    [Top of the Stack]
    5
    -2
    [Bottom of the Stack]
    

    而且,如果你是POP ,你可以检索-2 ,并且堆栈看起来和之前一样-2PUSH ed。

    堆叠的底部是可uppon移动的屏障PUSH ING和POP ING。 在大多数体系结构中,堆栈的底部由称为堆栈指针的处理器寄存器记录。 把它想象成一个unsigned char* 。 你可以减少它,增加它,做指针运算,等等。 所有事物的唯一目的是在堆栈的内容上做黑魔法。

    通过减少堆栈中自动变量的保留(空间)(记住,它向下增长),并通过增加它来释放它们。 以此为基础,先前的理论PUSH -2是伪组件中的简写:

    SUB %SP, $4    # Subtract sizeof(int) from the stack pointer
    MOV $-2, (%SP) # Copy the value `-2` to the address pointed by the stack pointer
    

    POP whereToPop仅仅是相反的

    MOV (%SP), whereToPop # Get the value
    ADD %SP, $4           # Free the space
    

    现在,编译func1()可能会产生下面的伪汇编( 注意 :你不应该完全理解这个):

    .rodata # Read-only data goes here!
    .STR0 = "hello" # The string literal goes here
    
    .text # Code goes here!
    func1:
        SUB %SP, $12     # sizeof(char*) + sizeof(char*) + sizeof(int)
        LEA .STR0, (%SP) # Copy the address (LEA, load effective address) of `.STR0` (the string literal) into the first 4-byte space in the stack (a.k.a `char *s`)
        PUSH $15         # Pass argument to `malloc()` (note: arguments are pushed last to first)
        CALL malloc
        ADD %SP, 4       # The caller cleans up the stack/pops arguments
        MOV %RV, 4(%SP)  # Move the return value of `malloc()` (%RV) to the second 4-byte variable allocated (`4(%SP)`, a.k.a `char *c`)
        PUSH (%SP)       # Second argument to `strcpy()`
        PUSH 4(%SP)      # First argument to `strcpy()`
        CALL strcpy
        RET              # Return with no value
    

    我希望这引起了你的一些光芒!


    我们需要考虑一个变量的内存位置和内容是什么。 记住这一点。

    对于一个int,变量有一个内存地址并且有一个数字作为它的内容。

    对于char指针,变量具有内存地址,其内容是指向字符串的指针 - 实际的字符串数据位于另一个内存位置。

    要理解这一点,我们需要考虑两件事情:

    (1) the memory layout of a program
    (2) the memory layout of a function when it's been called

    程序布局[典型]。 较低的内存地址到较高的内存地址:

    code segment -- where instructions go:
      ...
      machine instructions for func1
      ...
    data segment -- where initialized global variables and constants go:
      ...
      int myglobal_inited = 23;
      ...
      "hello"
      ...
    bss segment -- for unitialized globals:
      ...
      int myglobal_tbd;
      ...
    heap segment -- where malloc data is stored (grows upward towards higher memory
    addresses):
      ...
    stack segment -- starts at top memory address and grows downward toward end
    of heap

    现在这里是一个函数的堆栈框架。 它将在某个地方处于堆栈段内。 请注意,这是较低的内存地址:

    function arguments [if any]:
      arg2
      arg1
      arg0
    function's return address [where it will go when it returns]
    function's stack/local variables:
      char *s
      char *c
      int b
      char buf[20]
    

    请注意,我添加了“buf”。 如果我们改变了func1来返回一个字符串指针(例如“char * func1(arg0,arg1,arg2)”,我们添加了“strcpy(buf,c)”或者“strcpy(buf,c)”buf可以被func1使用。 func1可以返回c或s,但不是buf。

    这是因为使用“c”时,数据存储在数据段中,并在func1返回后保留。 同样,可以返回s,因为数据位于堆段中。

    但是,buf将不起作用(例如返回buf),因为数据存储在func1的堆栈帧中,并且当func1返回时弹出堆栈(这意味着它将显示为调用者的垃圾)。 换句话说,给定函数的堆栈帧中的数据可用于它以及它可能调用的任何函数[等等...]。 但是,该堆栈帧不适用于该函数的调用者。 也就是说,堆栈帧数据仅在被调用函数的生存期中“持续”。

    这是完全调整的示例程序:

    int myglobal_initialized = 23;
    int myglobal_tbd;
    
    char *
    func1(int arg0,int arg1,int arg2)
    {
        char *s = "hello";
        char *c;
        int b;
        char buf[20];
        char *ret;
    
        c = malloc(15);
        strcpy(c,s);
    
        strcpy(buf,s);
    
        // ret can be c, s, but _not_ buf
        ret = ...;
    
        return ret;
    }
    
    链接地址: http://www.djcxy.com/p/14075.html

    上一篇: where is memory allocated for pointers and their data?

    下一篇: How to know where memory is allocated for the reference and value type?