函数参数如何存储在内存中?

时间:2015-05-26 13:05:24

标签: c memory

在尝试使用stdarg.h宏替换变量参数函数,a.k.a。函数时,参数数量未知,我试图理解参数存储在内存中的方式。

这是一个MWE:

#include <stdio.h>

void    foo(int num, int bar1, int bar2)
{
  printf("%p %p %p %p\n", &foo, &num, &bar1, &bar2);
}

int main ()
{
  int     i, j;

  i = 3;
  j = -5;
  foo(2, i, j);
  return 0;
}

我理解没有任何问题,函数的地址与参数的地址不在同一个地方。 但后者并不总是以同样的方式组织起来。

在x86_32架构(mingw32)上,我得到了这样的结果:

004013B0 0028FEF0 0028FEF4 0028FEF8

表示地址的顺序与参数的顺序相同。

但是当我在x86_64上运行时,输出为:

0x400536 0x7fff53b5f03c 0x7fff53b5f038 0x7fff53b5f034

地址显然是相反的顺序w.r.t.争论。

因此我的问题是(tl; dr):

参数是否依赖于体系结构,还依赖于编译器?

4 个答案:

答案 0 :(得分:2)

他们是ABI依赖的。在无关紧要的情况下(仅以已知方式调用的函数),它完全依赖于编译器,通常意味着使用没有地址的寄存器(如果你要求那些参数将有一个地址那个地址,外观上说一切都有地址)。内联的函数甚至不再具有参数,所以它们的地址是什么的问题没有实际意义 - 尽管它们看起来存在并且在你强制发生这种情况时会有一个地址。

答案 1 :(得分:2)

它依赖于编译器。编译器供应商自然必须遵守CPU架构的规则。编译器通常也遵循平台ABI,至少对于可能与另一个编译器生成的代码进行互操作的代码。平台ABI是针对给定平台调用约定,链接语义等的规范。

E.g。 Linux和其他unix操作系统上的编译器都遵循System V Application Binary Interface,你会在3.2.3章中找到如何将参数传递给函数(寄存器中传递的参数从左到右传递,参数在内存中传递(在堆栈上)从右到左)。在Windows上,规则记录为here

答案 2 :(得分:1)

参数可能根本不存储在内存中,而是通过寄存器传递;但是语言需要为&的任何符号操作数返回一个地址,因此您的观察可能是您实际尝试观察的结果,并且编译器只是将值复制到这些地址以便它们可寻址

如果您以不同的顺序请求地址,例如:

,那么看看会发生什么可能会很有趣。
printf("%p %p %p %p\n", &num, &bar1, &bar2, &foo) ;

您可能会或可能不会得到相同的结果;关键是你所观察到的地址可能是观察的人工制品,而不是传递。当然在ARM ABI中,函数的前四个参数在寄存器R0,R1,R2和&amp;中传递。 R3,然后通过堆栈传递。

答案 3 :(得分:0)

在x86_64上,您以“怪异”的顺序获取参数,因为它们实际上并没有传递给任何内存中的函数。它们在cpu寄存器中传递。通过获取它们的地址,您实际上会强制编译器生成将参数存储在内存中的代码(在您的情况下在堆栈中),以便您可以获取它们的地址。

如果不与编译器交互,则无法实现stdarg宏。在gcc中,stdarg宏只是包装一个内置构造,​​因为在你需要它时,你无法知道参数可能在哪里(编译器可能已经重用了寄存器)。 gcc中内置的stdarg支持可以显着改变使用它们的函数的代码生成,以便参数可用。我认为其他编译器也是如此。