内存分配给指针和它们的数据在哪里?
我的问题是如果我有一些功能
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
一个值为5
的int
,你会得到:
[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。
堆叠的底部是可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?