C 调用约定和传递参数

发布于 2024-08-10 03:05:28 字数 375 浏览 10 评论 0原文

在 Linux(或 OS X)中进行函数调用时,被调用者可以修改堆栈上参数的值吗?我的假设是,由于调用者是清理它们的人,因此它们在函数调用后应该包含相同的值。但是我发现带有 -O2 的 GCC 正在修改在堆栈上传递给它的参数。我还查找了包括 System V i386 调用约定在内的文档,但无法找到对此问题的明确答案。

这是我正在调试的一些示例代码。

pushl %eax       # %eax = 0x28
call _print_any
popl %eax
                 # %eax is now 0x0a

我假设 GCC 修改堆栈上的参数是可以的,但我想知道在哪里指定它可以这样做。

When making a function call in Linux (or OS X for that matter), can the callee modify the values of the arguments on the stack? I was under the assumption that since the caller is the one that cleans them up, that they should contain the same values after the function call. However I found that GCC with -O2 was modifying parameters that were passed to it on the stack. I have also looked for documentation including the System V i386 calling conventions, but was unable to find a definitive answer to this.

Here is some sample code I was debugging.

pushl %eax       # %eax = 0x28
call _print_any
popl %eax
                 # %eax is now 0x0a

I would assume that GCC modifying that parameter on the stack is fine, but I want to know where it is specified that it can do so.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

魂牵梦绕锁你心扉 2024-08-17 03:05:28

尽管调用者(在某些调用约定中)是清理参数的人,但它实际上所做的只是释放之前在堆栈上分配的空间来保存参数值。被调用者可以在函数执行期间自由修改这些值,因为调用者稍后不会查看它们的值。

在您发布的示例中,GCC 发出了 popl %eax 指令来释放堆栈上参数占用的空间。它真正需要做的就是将 4 添加到 %esp(x86 上的堆栈在内存中向下增长),并且执行 popl %eax 指令是最短、最快的方法来做到这一点。如果编译器需要释放 20 个值,它可能会直接修改 %esp 而不是发出 20 个 popl 指令。

您可能会发现以下代码中没有使用 %eax 的新值。

Although the caller (in some calling conventions) is the one that cleans up the arguments, all it's really doing is deallocating the space previously allocated on the stack to hold the argument values. The callee is free to modify the values during execution of the function, because the caller isn't going to look at their values later.

In the example you posted, GCC has emitted the popl %eax instruction to deallocate the space taken by the parameter on the stack. All it really needs to do is add 4 to %esp (the stack on x86 grows downwards in memory), and executing the popl %eax instruction is the shortest and fastest way to do this. If the compiler needed to deallocate 20 values, it would probably modify %esp directly instead of emitting 20 popl instructions.

You will probably find that the new value of %eax is not used in the following code.

墨小沫ゞ 2024-08-17 03:05:28

是的,被调用者可以修改堆栈上的参数。对于被调用者而言,它们与局部变量相同。调用者确实清理了它们,但忽略了该值。

如果您谈论的是 C 或 C++ POD,清理只是修改堆栈指针。

如果您正在谈论带有析构函数的 C++,则调用者负责调用析构函数,但需要编写泛型类的析构函数来清除任何值。

Yes, the callee can modify the arguments on the stack. As far as the callee is concerned, they are the same as local variables. The caller does clean them up but ignores the value.

If you're talking C or C++ POD, clean up is simply modifying the stack pointer.

If you're talking about C++ with a destructor, the caller is responsible for invoking the destructor but destructors for generic classes need to be written to cleanup any value.

涙—继续流 2024-08-17 03:05:28

在标准 C 中,被调用者可以随意修改其参数的值,但调用者永远不会看到这些更改。

可能令人困惑的是,如果将 POINTER 传递给一个值,则被调用者可以通过取消引用该指针来更改该值,但如果被调用者实际上更改了指针本身,则调用者将看不到该更改。

一个小问题:C 标准不要求实现甚至拥有堆栈。

In standard C, the callee can modify the values of its arguments all it wants, but the caller will never see the changes.

What may be confusing is that if one passes a POINTER to a value, then the callee can change that value by dereferencing the pointer, but if the callee actually changes the pointer itself the caller will not see that change.

A small nit: the C standard does not require that the implementation even HAVE a stack.

油饼 2024-08-17 03:05:28

如果按值传递:

call do_it(to_it);

参数被复制(可能复制到堆栈的开头,但可能不取决于您的编译器),celled 程序可以随意修改此副本,但 clling 程序中的变量不会更改。

如果通过引用传递:

call do_it(&to_it);

则传递变量的地址。被调用变量所做的任何更改都将针对调用程序中的原始变量。

If you pass by value:

call do_it(to_it);

The argument is copied (probably to the begining of the stack but maybe not depending on your compiler) the celled program can mess with this copy as much as it wants but the variable in the clling program will not be changed.

If you pass by reference:

call do_it(&to_it);

Then the address of the variable is passed. Any changes the called variable makes will be to the original variable in the calling program.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文