为指针及其数据分配的内存在哪里?

时间:2015-10-04 17:37:17

标签: c string pointers memory memory-management

我的问题是我是否有一些功能

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

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

2 个答案:

答案 0 :(得分:1)

我们需要考虑变量的内存位置及其内容。记住这一点。

对于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)& #34;或" strcpy(buf,c)" ff1可以使用buf.func1可以返回c或s,但是不是 buf。

那是因为" c" 数据存储在数据段中,并在func1返回后持续存在。同样,可以返回s,因为数据在堆段中。

但是,buf不起作用(例如返回buf),因为数据存储在func1的堆栈帧中,当func1返回时,它会从堆栈中弹出[意味着它对调用者来说似乎是垃圾]。换句话说,给定函数的堆栈帧中的数据可用于它以及它可以调用的任何函数[等等......]。但是,该堆栈帧可供该函数的调用者使用。也就是说,堆栈帧数据仅仅是#34;持续存在"对于被调用函数的生命周期。

以下是经过全面调整的示例程序:

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;
}

答案 1 :(得分:0)

让我们从相同的东西的两个角度来区分这个答案,因为标准只会使对这个主题的理解复杂化,但无论如何它们都是标准:)。

两个部分共有的主题

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

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

第一部分:从一个独立的角度来看

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

因此,在您的示例中,您有三个自动变量:

  • char *s,初始化为"hello"的地址。
  • char *c,它会保留垃圾,直到它被后来的作业初始化。
  • int b,它一生中都有垃圾。

BTW ,标准未指定存储如何与函数配合使用。

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

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

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

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

例如,如果您PUSH int的值为5,则会获得:

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

然后,如果您PUSH -2

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

并且,如果您POP,则检索-2,并且堆栈看起来像-2 PUSH之前的那样。

堆栈的底部是一个障碍,可以通过PUSHPOP移动。在大多数体系结构中,堆栈的底部由processor register记录,称为堆栈指针。可以将其视为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

我希望这能为你带来一些启示!