Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/spring-boot/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
can';我不理解linux中有关函数调用的简单c代码的输出_C_Linux_Gcc_Compiler Construction - Fatal编程技术网

can';我不理解linux中有关函数调用的简单c代码的输出

can';我不理解linux中有关函数调用的简单c代码的输出,c,linux,gcc,compiler-construction,C,Linux,Gcc,Compiler Construction,当我试图理解函数调用时,我编写了一个简单的代码。但我不能理解它的输出 #include <stdio.h> int* foo(int n) { int *p = &n; return p; } int f(int m) { int n = 1; return 999; } int main(int argc, char *argv[]) { int num = 1; int *p = foo(num); int q

当我试图理解函数调用时,我编写了一个简单的代码。但我不能理解它的输出

#include <stdio.h>

int* foo(int n)
{
    int *p = &n;
    return p;
}

int f(int m)
{
    int n = 1;
    return 999;
}

int main(int argc, char *argv[])
{
    int num = 1;
    int *p = foo(num);
    int q = f(999);
    printf("[%d]\n[%d]\n", *p, q);
    /* printf("[%d]\n", *q); */
}
为什么
*p
是999

然后我修改了我的代码,如下所示:

#include <stdio.h>

int* foo(int n)
{
    int *p = &n;
    return p;
}

int f()
{
    int n = 1;
    return 999;
}

int main(int argc, char *argv[])
{
    int num = 1;
    int *p = foo(num);
    int q = f();
    printf("[%d]\n[%d]\n", *p, q);
    /* printf("[%d]\n", *q); */
}

为什么
*p
这里是1?我在Linux中使用gcc,但Clang得到了相同的输出。

您正在调用未定义的行为。您不能返回局部变量的地址(在本例中为参数
int n
),并期望它以后有用。

局部变量,如代码中的
n

int* foo(int n)
{
    int *p = &n;
    return p;
}
foo
函数完成后,“消失”

您不能使用它,因为访问该变量可能会产生不可预测的结果。不过,你可以这样写:

int* foo(int* n)
{
    *n = 999;
    return p;
}

int main(int argc, char *argv[])
{
    int num = 1;
    int *p = foo(&num);
    printf("[%d]\n", *p);
}

因为您的变量
num
在打印时仍然存在。

在您的第一个示例中,当您这样做时

int num = 1;
int *p = foo(num);
其中
foo()

int* foo(int n)
{
    int *p = &n;
    return p;
}
当从
main()
传递variabe
num
时,它将通过值传递给
foo
。换句话说,将在堆栈上创建名为
n
的变量
num
的副本。
num
n
都具有相同的值,但它们是不同的变量,因此具有不同的地址

当您从
foo()
返回
p
时,
main()
获取与
main()中的
num
delared地址不同的地址值

同样的解释也适用于修改后的程序

让我们看另一个例子来澄清:

int i = 2;

int * foo()
{
return &i;
}

int main() {

i = 1;
int *p = foo();
return 0;

}
在这种情况下,
i
在堆上声明,相同的
i
main()
foo()
中引用。相同的地址和值

让我们看第三个例子:

int i = 2;

int * foo(int i)
{
return &i;
}

int main() {

int i = 1;
int *p = foo(i);
return 0;

}
在这里,即使存在一个全局
i
,它也被
main()
中的局部变量
i
隐藏,这就是传递给
foo()
的内容。因此,从
foo
返回的
&i
,即
main()
p
的值将不同于我在main()中声明的变量的地址


希望这能澄清变量范围和按值传递的问题,

没有汇编器输出并不容易,但这是我的猜测:

局部变量和参数都在堆栈上。因此,当调用
foo
时,它将返回堆栈上第一个参数的地址

在第一个示例中,您将一个参数传递给第二个函数,该函数也将被推送到堆栈上,
p
恰好指向的位置。因此,它将覆盖
*p
的值


在第二个示例中,在第二个调用中未触及堆栈。旧值(num
)仍保留在那里。

除了您的代码由于返回堆栈变量的指针而引发未定义行为这一事实之外,您还询问了为什么行为会随着f()签名的更改而改变

原因是什么

原因在于编译器为函数构建stackframe的方式。假设编译器正在为foo()构建堆栈框架,如下所示:

对于f(int m),堆栈看起来很相似:

Address Contents  
0x199   local variable n
0x200   Saved register A that gets overwritten in this function
0x201   parameter m
0x202   return value
0x203   return address
现在,如果在foo中返回指向“n”的指针,会发生什么?结果指针将为0x201。返回foo后,堆栈顶部位于0x204。内存保持不变,您仍然可以读取值“1”。在调用另一个函数(在您的例子中为“f”)之前,这一直有效。调用f后,位置0x201被参数m的值覆盖

如果您访问此位置(并且使用printf语句),它将显示“999”。如果在调用f()之前复制了此位置的值,则会找到值“1”

按照我们的示例,f()的stackframe如下所示,因为没有指定任何参数:

Address Contents  
0x200   local variable n
0x201   Saved register A that gets overwritten in this function
0x202   return value
0x203   return address
在使用“1”初始化局部变量时,可以在调用f()后读取位置0x200处的“1”。如果现在从位置0x201读取该值,则将获得已保存寄存器的内容

一些进一步的声明

  • 理解上面的解释是向你展示为什么你观察你所观察的东西的方法,这一点至关重要
  • 真正的行为取决于您使用的工具链和所谓的调用转换
  • 人们可以很容易地想象,有时很难预测会发生什么。这与释放内存后访问内存的情况非常相似。这就是为什么发生的事情通常是不可预测的
  • 这种行为甚至可以随着优化级别的改变而改变。例如,我可以想象,如果你打开-O3,观察结果将不同,因为未使用的变量n将不再出现在二进制文件中
  • 理解了背后的机制之后,应该可以理解为什么对从foo检索到的地址进行写访问会导致严重问题
为那些试图通过实验证明这一解释的勇敢者

首先,重要的是要看到上面的解释并不依赖于真正的堆栈帧布局。为了便于理解,我刚刚介绍了布局

如果你想在你自己的机器上测试这个行为,我建议你使用你最喜欢的调试器,看看本地变量和参数所在的地址,看看到底发生了什么。请记住:更改f的签名会更改堆栈上的信息。因此,唯一真正的“可移植”测试是更改f()的参数,并观察p指向的值的输出

在调用f(void)的情况下,放在堆栈上的信息差别很大,在p指向的位置写入的值不一定取决于参数
Address Contents  
0x199   local variable p
0x200   Saved register A that gets overwritten in this function
0x201   parameter n
0x202   return value
0x203   return address
Address Contents  
0x199   local variable n
0x200   Saved register A that gets overwritten in this function
0x201   parameter m
0x202   return value
0x203   return address
Address Contents  
0x200   local variable n
0x201   Saved register A that gets overwritten in this function
0x202   return value
0x203   return address
int *p = foo(num);
int q = f(999);