我的问题是我是否有一些功能
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变量怎么样?
答案 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
之前的那样。
堆栈的底部是一个障碍,可以通过PUSH
和POP
移动。在大多数体系结构中,堆栈的底部由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
我希望这能为你带来一些启示!