无法理解的简单的C code约在Linux函数调用输出 [英] can't understand the output of the simple c code about function call in linux

查看:126
本文介绍了无法理解的简单的C code约在Linux函数调用输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写一个简单的code,当我试图理解函数调用。但我不明白它的输出。

I write a simple code when I try to understand the function call. But I can't understand it's output.

#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); */
}

输出:

[999]
[999]

为什么 * P 999?

然后我修改了code像如下:

Then I modified my code like follows:

#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); */
}

输出:

[1]
[999]

为什么 * P 这里是1?我在Linux下使用gcc但锵得到了相同的输出。

Why *p is 1 here? I'm in Linux, using gcc but Clang got the same output.

推荐答案

除了一个事实,即你的code为provking不确定的行为,因为你是返回一个指向一个堆栈变量,你问为什么与行为变化改变f的签名()。

Aside the fact that your code is provking undefined behaviour because you are returning a pointer to a stack variable, you were asking for why the behavior changes with changing the signature of f().

之所以

原因在于编译器生成的功能,通过StackFrame的方式。假设编译器正在建设的堆栈帧foo的如下():

The reason lies in the way the compiler builds the stackframe for the functions. Assume the compiler is building the stack frame as follows for foo():

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

和为F(INT米)堆栈看上去文文静静的相似:

And for f(int m) the stack looks quiet similar:

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

现在,如果您在富返回一个指针,以'N'发生了什么?由此产生的指针将是0x201。返回foo的后堆栈的顶部是在量0x204。内存保持不变,你仍然可以读出的值1。这工作,直到调用另一个功能(在你的情况'F')。在调用F后,进位置0x201将覆盖参数m的值。

Now, what happens if you return a pointer to 'n' in foo? The resulting pointer will be 0x201. After returning foo the top of the stack is at 0x204. The memory remains unchanged and you can still read the value '1'. This works until calling another function (in your case 'f'). After calling f, the location 0x201 is overwritten with the value for parameter m.

如果您访问这个位置(和你与你的printf语句做)读取999。如果你在调用之前˚F复制这个位置的值(),你会发现值为1。

If you access this location (and you do with your printf statement) it reads '999'. If you had copied the value of this location before invoking f() you would have found the value '1'.

坚持我们的例子中,对于f()通过StackFrame是这样的,因为没有指定的参数:

Sticking to our example, the stackframe for f() would look like this as there are no parameters specified:

Address Contents  
0x200   local variable n
0x201   Saved register A that gets overwritten in this function
0x202   return value
0x203   return address

当你与初始化局部变量1就可以在位置为0x200调用˚F后读'1'()。如果你现在读的位置0x201值,你会得到一个保存的寄存器的内容。

As you are initializing the local variable with '1' you can read '1' at location 0x200 after invoking f(). If you now read the value from location 0x201 you'll get the contents of a saved register.

一些进一步的声明


  • 关键是要明白的是,上述的解释是向您展示为什么你观察你观察到了什么的方法。

  • 真正的行为取决于你所使用的工具链和所谓的调用convetions。

  • 人们可以很容易想象,它有时很难predict会发生什么。这是一个安静的类似情况,以释放后访问内存。这就是为什么它在一般非predictable会发生什么。

  • 这种行为甚至可以改变优化级别更改。例如。我可以想像,如果打开-O3例如,观察会有所不同,因为未使用的变量n将不会在二进制再出现。

  • 已经了解了背后的机制,应该怎么写访问从foo检索到的地址可能会导致严重的问题是可以理解的。

对于胆大试图通过实验证明这一点的解释

首先,它看到上面的解释不依赖于一个真实的栈帧布局是很重要的。我刚才介绍的布局,以便有一个例子很容易理解。

First of all it's important to see that above explaination does not rely on a real stack frame layout. I just introduced the layout in order to have a illustration easy to understand.

如果你想测试自己的机器上的行为,我建议你把你最喜欢的调试器,并期待在那里的局部变量和参数都放在看看到底发生了什么地址。请记住:改变f的签名更改放在堆栈上的信息。因此,唯一的真正便携式测试正在改变对于f(的参数)和观察的值p指向的输出。

If you want to test the behavior on your own machine i suggest you take your favourite debugger and look at the addresses where the local variables and the parameters are placed to see what really happens. Keep in mind: Changing the signature of f changes the information placed on the stack. So the only real "portable" test is changing the parameter for f() and observe the output for the value p points to.

在调用F(空隙)把堆栈信息的情况下,大量的不同,并在位置p写入的值是指向不一定依赖于参数或当地人了。它也可以依赖于堆栈变量从主功能

In the case of calling f(void) the information put on the stack differs massively and the value written at the position p is pointing to does not necessarily depend on the parameters or locals anymore. It can also depend on stack variables from the main-function.

在我的机器为例再现透露,1你在第二个变型读取来自储蓄被用于存储寄存器1为民,因为它似乎是用来装载ñ。

On my machine for example the reproduction revealed that the '1' you read in the second variant comes from saving the register that was used to store '1' to "num" as it seems to be used for loading n.

我希望这给你一些启示。发表评论,如果您还有其他问题。 (我知道这是有点怪异的理解)

I hope this gives you some insight. Leave a comment if you have further questions. (I know this is somewhat weird to understand)

这篇关于无法理解的简单的C code约在Linux函数调用输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆