请解释程序的工作原理

时间:2010-07-05 06:29:59

标签: c

#include<stdio.h>
int f();

int main()
{

    f(1);
    f(1,2);
    f(1,2,3);
}

f(int i,int j,int k)
{

    printf("%d %d %d",i,j,k);

}

运行正常(没有任何错误)...你可以解释它是如何执行的吗? f(1)和f(1,2)如何链接到f(int,int,int)?

6 个答案:

答案 0 :(得分:14)

你必须对“错误”有不同的定义:-)在你调用f函数的前两次打印什么?我得到了

1 -1216175936 134513787
1  2          134513787
1  2          3

我的三个函数调用。

你所看到的是从C的早期阶段开始的延续,当人们通过他们的函数调用时,他们可以自由自在地玩耍。

所有发生的事情是你正在调用一个函数f并且它从堆栈中打印出三个值(是的,即使你只给它一两个)。当你没有提供足够的东西时会发生什么,你的程序最有可能只是使用那里的东西,通常会在阅读时导致数据问题,写作时会出现灾难性的失败。

这是完全可编译的,虽然非常不明智,C。我的意思是在一个非常真实的“未定义的行为”中,这个词的意义(具体指C99:“如果表示被调用函数的表达式有一个类型不包含原型,...如果参数的数量不等于参数的数量,则行为未定义“)。

你应该提供完整形成的功能原型,例如:

void f(int,int,int);

确保编译器解决此问题,并在变量参数函数中使用省略号(...)。


顺便说一句,通常在幕后发生的事情是调用函数以如下堆栈开头:

12345678
11111111

并将(例如)两个值推送到堆栈上,以便最终得到:

12345678
11111111
2
1

当被调用的函数使用堆栈上的前三个值时(因为这是它想要的),它会发现它有1211111111

它完成它必须做的事情然后返回并且调用函数从堆栈中清除这两个值(这称为调用者制造良好策略)。哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇

答案 1 :(得分:4)

此声明:

int f();

...告诉编译器“f是一个函数,它接受一些固定数量的参数,并返回int”。然后尝试用一个,两个和三个参数调用它 - C编译器在概念上是一次通过(在预处理之后),所以此时,编译器没有可用的信息与你争论。

f()的实际实现需要三个int参数,因此只提供一个和两个参数的调用会调用未定义的行为 - 这是一个错误,这意味着编译器不需要给你运行程序时可能会出现错误消息,任何

答案 2 :(得分:3)

int f();

在C中,这声明了一个函数,它接受可变数量的参数,即它等同于C ++中的以下内容

int f(...);

要检查此项,请使用以下内容代替int f();

int f(void);

这会导致编译器抱怨。

请注意:此处还涉及C链接器怪癖... C链接器不会验证在调用时传递给函数的参数,只是链接到具有相同名称的第一个公共符号。因此,由于int f()的声明,允许在main中使用f()。但是链接器在调用站点的链接时间内绑定函数f(int,int,int)。希望有道理(如果没有,请告诉我)

答案 3 :(得分:1)

它运行正常,因为int f()表示其他答案已经说过:它表示未指定数量的参数。这意味着您可以使用所需的参数数量(也超过3)调用它,而编译器不会对其进行任何说明。

它在“封面下”工作的原因是,参数被压入堆栈,然后从f函数中的“堆栈”访问。如果传递0个参数,则函数的i, j, k“对应”堆栈上的值,从函数PoV开始,这些值是垃圾。尽管如此,您可以访问他们的价值观。如果传递1个参数,则三个i j k中的一个访问该值,其他参数变为垃圾。等等。

请注意,如果参数以其他方式传递,则相同的推理也有效,但无论如何这些都是正在使用的约定。这些约定的另一个重要方面是被调用者不负责调整堆栈;这取决于调用者,知道有多少参数被推送到真实状态。如果不是这样,f的定义可能表明它必须“调整”堆栈以“释放”三个整数,这将导致某种崩溃。

你写的内容对于当前标准来说没什么问题(在gcc编译时即使用-std=c99 -pedantic也没有警告;有警告,但它是关于{{1}前面缺少的int虽然很多人觉得它很恶心,并称之为“过时功能”。当然,您在示例代码中的使用并未显示任何有用性,并且可能有助于破坏错误更具约束力的原型使用! (但是,我更喜欢C到Ada)

添加

不会触发“未定义行为”问题的“功能”更“有用”的用法可能是

f

答案 4 :(得分:0)

使用g ++编译器编译同一程序时,您会看到以下错误 -

g++ program.c
program.c: In function `int main()':
program.c:2: error: too many arguments to function `int f()'
program.c:6: error: at this point in file
program.c:2: error: too many arguments to function `int f()'
program.c:7: error: at this point in file
program.c:2: error: too many arguments to function `int f()'
program.c:8: error: at this point in file
program.c: At global scope:
program.c:12: error: ISO C++ forbids declaration of `f' with no type

使用带有选项-std = c99的gcc只会发出警告

使用与g ++默认具有相同标准的相同程序编译相同的程序,提供以下消息:

gcc program.c -std=c++98
cc1: warning: command line option "-std=c++98" is valid for C++/ObjC++ but not for C

我的回答是,或者编译器符合不同的标准,这个标准不像c ++符合的标准那样具有限制性。

答案 5 :(得分:0)

在C语句中,声明必须至少声明返回类型。所以

int f();

声明一个返回类型int的函数。该声明不包含有关该函数所用参数的任何信息。函数的定义是

f(int i,int j,int k)
{

    printf("%d %d %d",i,j,k);
}

现在众所周知,该函数需要三个int s。如果使用与定义不同的参数调用函数,则不会出现编译时错误,但会出现运行时错误(或者如果您不喜欢错误的负面含义:“未定义的行为”)。标准强制C编译器来捕获这些不一致。

为了防止这些错误,您应该使用正确的函数原型,例如

f(int,int,int);           //in your case
f(void);                  //if you have no parameters