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

我的问题是我是否有一些function

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变量怎么样?

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

两个部分共同的主题

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

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

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

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

  • char *s ,它被初始化为"hello"的地址。
  • char *c ,它保存垃圾直到它被后来的赋值初始化。
  • int b ,它在其整个生命周期中保存垃圾。

顺便说一下,标准没有规定存储如何与function协同工作。

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

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

堆栈以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 ,并且堆栈看起来像-2之前的PUSH ed。

堆栈的底部是一个屏障,可以通过PUSHPOP进行移动。 在大多数体系结构中,堆栈的底部由称为堆栈指针的处理器寄存器记录。 把它想象成一个unsigned char* 。 你可以减少它,增加它,对它做指针算术,等等。 一切都是为了在堆栈的内容上做黑魔法。

在堆栈中保留(空间)自动变量是通过减少它(记住,它向下增长)来完成的,并且通过增加它来释放它们。 基于此,先前的理论PUSH -2在伪assembly中是这样的简写:

 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只是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 (aka `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)`, aka `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)程序的内存布局
 (2)函数调用时的内存布局 

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

 代码段 - 指令的位置:
   ...
   func1的机器说明
   ...
数据段 - 初始化的全局变量和常量去:
   ...
   int myglobal_inited = 23;
   ...
   “你好”
   ...
 bss段 - 用于单位化全局:
   ...
   int myglobal_tbd;
   ...
堆段 - 存储malloc数据的位置(朝向更高的内存增长)
地址):
   ...
堆栈段 - 从顶部内存地址开始,向下逐渐增长
堆 

现在这是一个函数的堆栈框架。 它将在某个堆栈段内。 注意,这是更低的内存地址:

 函数参数[如果有的话]:
   ARG2
   ARG1
  为arg0
函数的返回地址[返回时将返回的位置]
函数的堆栈/局部变量:
   char * s
   char * c
   int b
   char buf [20]

请注意,我添加了“buf”。 如果我们改变func1以返回一个字符串指针 (例如“char * func1(arg0,arg1,arg2)”并且我们添加了“strcpy(buf,c)”或“strcpy(buf,c)”,则ff1可以使用buf。 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; }