我想知道C编译器(我试过gcc和clang)如何在内部组织函数的参数列表。为此,我制作了一个由两个文件组成的程序,例如:
foo.c:
double foo (double (*f) (void *, double), void * arg, double x)
{
return f (arg, x);
}
main.c:
#include <stdio.h>
extern double foo (double (*) (double), double);
double bar (double x)
{
return x + x;
}
int main ()
{
double x = foo (&bar, 2);
printf ("%g\n", x);
return 0;
}
并使用参数列表和foo.c中foo
的正文进行游戏。当foo.c与main.c中的定义不一致时,程序的行为应根据C标准(如果我理解正确?)来定义。
我尝试了几种变体(包括上面的变体),程序将打印4。 其中一个更具异国情调的是
double foo (double x, double (*f) (char, double, int), int r)
{
char a;
return f (a, x, r);
}
。但是,如果我尝试类似
的话double foo (double x, double (*f) (char, double, double), double y)
{
char a;
return f (a, y, x);
}
结果不会是4,但如果我写f(a, x, y)
,我会再次获得4。
该实验让我认为参数列表在内部表示为对应于不同类型的数组列表,其中有关不同类型的参数顺序的信息将丢失。例如,参数列表
(char a, double x, int i, double y, char b)
类似于(char:{a, b}, int:{i}, double:{x, y})
,(double z, char c)
的投射等于(char:{c = a}, double:{z = x})
,我将T:{}
定义为T
类型的数组}。
所以:
我的解释是否正确?
这种行为是否在某个地方标准化了?
我可以依赖这样的行为多少?
此行为允许一些通用编程。有人在实践中利用它吗?
谢谢!
答案 0 :(得分:3)
如果函数的签名不匹配,则不能依赖任何行为。你的程序可以正常工作,崩溃或发射火箭导弹。这是未定义行为的定义。
如果你使用variadict函数,或者你没有描述参数的函数(即f(),你不能依赖某些东西,不要与f(void)混淆)。对于那些,有关于论点推广和转换的规则。寻找C99标准了解更多。
剩下的就是纯粹的猜测。如果您知道处理器的汇编语言是如何工作的,则可以观察并推断出某些行为。例如,在x86中,调用函数和传递参数的标准方法已经很好地定义了......
无论如何,依靠未定义的行为并不是一个好主意。