函数转换和C中函数参数列表的内部表示

时间:2013-01-17 13:18:16

标签: c pointers gcc casting clang

我想知道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类型的数组}。

所以:

我的解释是否正确?

这种行为是否在某个地方标准化了?

我可以依赖这样的行为多少?

此行为允许一些通用编程。有人在实践中利用它吗?

谢谢!

1 个答案:

答案 0 :(得分:3)

如果函数的签名不匹配,则不能依赖任何行为。你的程序可以正常工作,崩溃或发射火箭导弹。这是未定义行为的定义。

如果你使用variadict函数,或者你没有描述参数的函数(即f(),你不能依赖某些东西,不要与f(void)混淆)。对于那些,有关于论点推广和转换的规则。寻找C99标准了解更多。

剩下的就是纯粹的猜测。如果您知道处理器的汇编语言是如何工作的,则可以观察并推断出某些行为。例如,在x86中,调用函数和传递参数的标准方法已经很好地定义了......

无论如何,依靠未定义的行为并不是一个好主意。