理解C语言中的函数原型

时间:2011-08-15 18:26:04

标签: c function-prototypes

1)以下代码:

A)

void main()
{
float x;
fun(x,x,x);
}
fun(float x,float x){}

可能不起作用,因为实际参数被提升为double,这与函数定义不匹配,对吧?

b)现在,

void main()
{
float x;
fun(x,x,x);
}
fun(double x,double x){}

工作。当编译器可以检查数据类型时,为什么它不能检查参数的数量?根据定义必须只有两个!!!?

2)此外,允许非原型的意义是什么 void fun(); 他们有什么区别,当他们没有帮助检查函数调用传递的参数时,为什么它们被支持?

感谢:)

6 个答案:

答案 0 :(得分:1)

我会再次回答(希望不要被标记下来)。因为你似乎仍然感到困惑。

C语言遗产也让人感到困惑,因为在C的早期版本中允许使用某些东西,这些东西现在被认为是不好的做法。但出于兼容性原因,他们仍然被允许。 C ++不允许它们(因此我在其他答案中的评论)。

要知道的第一件事是c和c ++从上到下读取源文件以确定你在做什么;他们不期待,如果他们找到新的信息,他们不会回去调整他们的解释(与C#形成鲜明对比)。

所以在编译器看到

的代码中
fun(x,x,x);

它问自己。 “我知道'有趣'是什么吗?答:没有。 c ++编译器将在那时放弃

x.cpp: In function ‘int main()’:
x.cpp:4: error: ‘fun’ was not declared in this scope

为了解决这个问题,你必须在尝试使用之前声明乐趣(意思是在源文件的前面)。你这样做是通过

来实现的
void fun(float, float);

在文件的开头。这对编译器说“某个地方会有一个名为'fun'的函数需要2个浮点数”,编译器知道要做以后看到有趣的调用。

那么C. C尝试提供帮助(或不依赖于你的观点)

C试图猜测你的意思(鉴于你没有告诉它)

fun(x,x,x);

它的默认值是假设fun是一个需要3个int并返回int的函数。它的观点是,它可能是正确的,如果它错了,那么你将解决它 注意 - 它没有查看您尝试传递给乐趣的参数。它只是决定所有未知函数采用并返回整数。

所以现在你需要升级规则等。你试图通过向它传递浮点数或双精度来调用一个函数来获取整数。这将起作用

在你的问题中,你说浮动失败但双重工作。在我的测试中都编译,但我收到编译器警告。但是,你对fun的定义没有返回类型(在这种情况下为void)。如果我使用您的确切语法,那么既不编译

建议,始终先声明功能。使用C ++作为“更好的C”编译器。它更严格,更有帮助。

答案 1 :(得分:0)

这正确编译。你正在调用一个未定义的函数,它在现代编译器上是一个警告。

它会自动将float提升为double,char和short为int并假设返回int。

第二个例子有效,即使参数数量错误,因为有太多,正常的调用约定会默默地纠正。

void fun();是一个旧式的原型,它只声明了返回类型,但没有说明参数。

答案 2 :(得分:0)

原型必须在函数调用之前出现才有用;要么在调用之前定义函数:

void fun (float x, float y) {} // IMPLICIT TYPING IS BAD JUJU!

int main(void) // Unless the documentation for your compiler *explicitly* says
               // that "void main()" is a legal signature, main should
               // always return int.
{
  float x;
  fun(x,x,x);
  return 0;
}

或单独声明:

int main(void)
{
  void fun(float x, float y);
  float x;
  fun(x,x,x);
  return 0;
}

void fun(float x, float y) {}

我更喜欢第一个版本。

答案 3 :(得分:0)

其他人已经解释了为什么它做了它所做的事情,但值得一提的是,这就是为什么许多C程序从头到尾阅读的原因。以下将有正确和预期的结果(主要的返回类型是int,但这只是迂腐):

fun(float x,float x){}

void main()
{
    float x;
    fun(x,x);
}

通过按此顺序定义函数,您可以消除几乎所有函数的原型需求(相互递归是一个例外)。然后,您可以将API函数的原型放在标题中,并使本地函数保持静态。

答案 4 :(得分:0)

  

当编译器可以检查数据类型时,为什么不能检查参数的数量?根据定义必须只有两个!!!?

这是卓越的未定义行为。编译器允许您这样做,因为它假定您知道您在做什么。如果你提出警告级别并且没有得到警告,我会切换到另一个编译器。假设你知道自己在做什么,这是一回事。但是,假设你没有犯任何错误是另一回事,不要警告你。就像你已经指出的那样,技术上可以做到这一点,而且并不困难。

  

允许非原型如void fun()的意义是什么?他们有什么区别,当他们没有帮助检查函数调用传递的参数时,为什么它们被支持?

首先,它们是为了向后兼容。旧的预标准C没有原型。其次,拥有一个不包含原型的函数指针可能很有用:

void f(int i) { /* ... */ }
void g(double d) { /* ... */ }

int main(void) {
  typedef void ftype();
  ftype *ptr[] = { &f, &g };
  ptr[0](42);
  ptr[1](3.1415);
}

该代码有效且其行为已明确定义。

答案 5 :(得分:-1)

两者都应该无法编译;您正在使用什么编译器

另外 - 你没有使用函数原型;函数原型是调用之前调用函数的规范