c/c++开发分享调用C函数,该函数不带参数参数

关于C调用约定和64/32位编译之间可能存在未定义的行为,我有一些奇怪的问题。 首先是我的代码:

int f() { return 0; } int main() { int x = 42; return f(x); } 

正如你所看到的,我用参数调用f,而f不带参数。 我的第一个问题是这个论点在调用时是否真的给了f。

神秘的线条

经过一点点objdump我得到了好奇的结果。 传递x作为f的参数:

 00000000004004b6 : 4004b6: 55 push %rbp 4004b7: 48 89 e5 mov %rsp,%rbp 4004ba: b8 00 00 00 00 mov $0x0,%eax 4004bf: 5d pop %rbp 4004c0: c3 retq 00000000004004c1 : 4004c1: 55 push %rbp 4004c2: 48 89 e5 mov %rsp,%rbp 4004c5: 48 83 ec 10 sub $0x10,%rsp 4004c9: c7 45 fc 2a 00 00 00 movl $0x2a,-0x4(%rbp) 4004d0: 8b 45 fc mov -0x4(%rbp),%eax 4004d3: 89 c7 mov %eax,%edi 4004d5: b8 00 00 00 00 mov $0x0,%eax 4004da: e8 d7 ff ff ff callq 4004b6  4004df: c9 leaveq 4004e0: c3 retq 4004e1: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 4004e8: 00 00 00 4004eb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 

不传递x作为参数:

 00000000004004b6 : 4004b6: 55 push %rbp 4004b7: 48 89 e5 mov %rsp,%rbp 4004ba: b8 00 00 00 00 mov $0x0,%eax 4004bf: 5d pop %rbp 4004c0: c3 retq 00000000004004c1 : 4004c1: 55 push %rbp 4004c2: 48 89 e5 mov %rsp,%rbp 4004c5: 48 83 ec 10 sub $0x10,%rsp 4004c9: c7 45 fc 2a 00 00 00 movl $0x2a,-0x4(%rbp) 4004d0: b8 00 00 00 00 mov $0x0,%eax 4004d5: e8 dc ff ff ff callq 4004b6  4004da: c9 leaveq 4004db: c3 retq 4004dc: 0f 1f 40 00 nopl 0x0(%rax) 

所以我们可以看到:

  4004d0: 8b 45 fc mov -0x4(%rbp),%eax 4004d3: 89 c7 mov %eax,%edi 

当我用x调用f但是因为我在汇编方面不是很好时,我真的不理解这些行。

64/32位矛盾

否则我尝试了其他的东西并开始打印我的程序堆栈。

用x给出的堆栈(以64位编译):

 Address of x: ffcf115c ffcf1128: 0 0 ffcf1130: -3206820 0 ffcf1138: -3206808 134513826 ffcf1140: 42 -3206820 ffcf1148: -145495616 134513915 ffcf1150: 1 -3206636 ffcf1158: -3206628 42 ffcf1160: -143903780 -3206784 

堆栈与x没有给f(编译为64位):

 Address of x: 3c19183c 3c191818: 0 0 3c191820: 1008277568 32766 3c191828: 4195766 0 3c191830: 1008277792 32766 3c191838: 0 42 3c191840: 4195776 0 

由于某种原因,在32位x似乎是推动堆栈。

堆栈与x给f(编译为32位):

 Address of x: ffdc8eac ffdc8e78: 0 0 ffdc8e80: -2322772 0 ffdc8e88: -2322760 134513826 ffdc8e90: 42 -2322772 ffdc8e98: -145086016 134513915 ffdc8ea0: 1 -2322588 ffdc8ea8: -2322580 42 ffdc8eb0: -143494180 -2322736 

为什么x会出现在32而不是64?

印刷代码: http : //paste.awesom.eu/yayg/QYw6&ln

我为什么要问这样愚蠢的问题?

感谢您抽出时间阅读,直到这里,帮助我理解某些内容或让我意识到我的问题毫无意义。

    答案是,正如您所怀疑的,您正在做的是未定义的行为(在传递多余的参数的情况下)。

    然而,许多实现中的实际行为是无害的。 在堆栈上准备参数,被调用函数忽略。 被调用的函数不负责从堆栈中删除参数,因此没有任何危害(例如不平衡的堆栈指针)。

    这种无害的行为使得C黑客一度开发了一个变量参数列表工具,该工具曾经在Unix C库的古老版本中位于#include 之下。

    这演变为ANSI C

    这个想法是:将额外的参数传递给函数,然后动态地遍历堆栈以检索它们。

    那今天不行。 例如,正如您所看到的,该参数实际上并未放入堆栈,而是加载到RDI寄存器中。 这是GCC在x86-64上使用的约定。 如果你在堆栈中游走,你将找不到前几个参数。 相比之下,在IA-32上,GCC使用堆栈传递参数:尽管您可以使用“fastcall”约定获得基于寄存器的行为。

    va_arg宏将正确地考虑混合寄存器/堆栈参数传递约定。 (或者,相反,当您对可变参数函数使用正确的声明时,它可能会抑制寄存器中尾随参数的传递,因此va_arg 可以通过内存进行va_arg 。)

    如果您添加了一些优化,那么您的机器代码可能更容易理解。 例如,序列

      4004c9: c7 45 fc 2a 00 00 00 movl $0x2a,-0x4(%rbp) 4004d0: 8b 45 fc mov -0x4(%rbp),%eax 4004d3: 89 c7 mov %eax,%edi 4004d5: b8 00 00 00 00 mov $0x0,%eax 

    由于看起来像一些浪费的数据移动,它是相当迟钝的。

    如何将参数传递给函数取决于平台ABI(应用程序二进制接口)。 ABI可以使用编译器X编译库,并将它们与使用编译器Y编译的代码一起使用。这些都不是由标准定义的。

    标准没有要求“堆栈”甚至存在,更不用说它用于函数调用。

    x86芯片的寄存器数量有限,ABI反映了这一事实; 正常的32位x86调用约定将堆栈用于所有参数。

    64位架构不是这种情况,它有更多的寄存器,并使用其中一些寄存器用于前几个参数。 这显着加快了函数调用。

    类似地,Windows 32位“fastcall”调用约定在寄存器中传递一些参数。 (为了使用非标准调用约定,您需要适当地注释函数声明,并在定义函数声明的位置一致。)

    您可以在此Wikipedia文章中找到有关各种调用约定的更多信息。 AMD64 ABI可以在x86-64.org上找到(PDF文档) 。 最初的System V IA-32 ABI(Linux,xBSD和OS X上使用的ABI的基础)仍然可以从www.sco.com访问(PDF文档) 。


    未定义的行为?

    OP中提供的代码肯定是未定义的行为。

      以上就是c/c++开发分享调用C函数,该函数不带参数参数相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注(猴子技术宅)。

      本文来自网络收集,不代表猴子技术宅立场,如涉及侵权请点击右边联系管理员删除。

      如若转载,请注明出处:https://www.ssfiction.com/c-cyuyankaifa/550171.html

      发表评论

      邮箱地址不会被公开。 必填项已用*标注