C函数接受值,即使其参数是void。
C函数接受值,即使其参数是void。
我是新手程序员,之前我读到在定义一个函数之前,我们需要定义它的原型。如果我们不想让函数接受任何值,我们就将参数写为void。所以为了检查一下,我写了这个小程序,在定义函数原型时,我将参数写为void,然后在定义中写int a作为参数(希望会得到一个错误,但我从来没有得到过)。然后我编译并运行了这个程序,它给了我以下输出:
这个程序打印从1到3的所有数字的平方
1的平方是1:
2的平方是4:
3的平方是9:
之后我将定义中的参数改为float a(只是为了检查程序是否仍然能工作),这次我得到了一个不同的输出,如下所示:
这个程序打印从1到3的所有数字的平方
1的平方是0:
2的平方是0:
3的平方是0:
请问有人能解释一下这里发生了什么吗?为什么我的编译器没有报错,为什么我得到完全不同的int和float输出?我使用的是带有更新4的Visual Studio Professional 2013!
从上述内容中可以得出以下结论:
问题的出现原因是旧的C标准允许声明没有参数的函数,此时参数类型默认为int。只要一切匹配,一切都可以正常工作,但如果不匹配,就会出现奇怪的情况。
这一切都归结于调用约定。调用代码将参数放在某个地方(通常是栈和寄存器),然后函数从某个地方取参数,如果这个“某个地方”相同,并且数据类型匹配,一切都可以正常工作。而当它们不匹配时,就会出现未定义行为。
所以int版本可以正常工作是因为一切匹配。编译器会警告你它无法知道你的代码是否实际上能正常工作(或者在后续的C标准版本和C++中会给出实际错误),但这只是警告,意味着你需要确保一切正常(你实际上不想这样做,这是编译器的工作,所以请启用警告并修复它们!)。
你提到的float版本则是未定义行为。计算机可以做任何事情(包括在你下次使用网银时破解你的银行账户并将其清空,这基本上就是许多恶意软件实际上感染计算机的方式,利用编程错误)。但在这里,你很可能会得到零,因为浮点参数和返回值是通过FPU寄存器传递的,而函数没有改变这些寄存器,所以返回值恰好是零。
C函数接受参数为void时,仍然可以接受值的原因是声明和定义之间的不一致性。下面是解决此问题的方法。
首先,根据C标准,编译器必须对你问题中的程序发出至少一条诊断消息。你的`square`函数被声明为`int square(void);`,意味着它不接受任何参数并返回一个整数结果。然后你调用它时使用了`square(i)`,这里传递了一个参数。这是一个约束性违规,意味着编译器必须发出一条诊断消息(可以是警告或致命错误)。
然后你使用了旧式的定义方式`square(a) { /* ... */ }`,它使用了过时的“隐式int”规则,表示`square`接受一个整数参数并返回一个整数结果。但是在调用的地方,此定义是不可见的,所以编译器将只处理声明。
根据Microsoft的文档,`#pragma warning (disable:4996)`可以禁用“弃用”函数的警告。它不应该应用于你的程序中的任何内容,所以你应该仍然会在不正确的调用`square(i)`上得到警告或致命错误。
如果你的编译器没有警告或错误地编译,那么可能是你的编译器有问题,或者你的问题中的代码或你对编译器行为的描述是不正确的。
至于为什么使用`int`和`float`会得到不同的输出,如果函数的声明和定义不一致,任何调用都会导致未定义的行为。当你将定义更改为使用`float`时,生成的代码可能会传递一个`int`值,函数错误地将其解释为一个`float`值,结果是没有意义的。
更新:现在我已经看到了你的屏幕截图,它看起来你的编译器做了我没有预料到的事情。它警告说声明`int square(void)`有一个void参数列表。单独来看,这个声明是完全有效的;只有因为接下来的定义将`square`定义为一个`int`参数,它才成为一个问题。它必须已经决定在看到不兼容的定义之后才警告声明。
真正让我惊讶的是它没有警告关于调用`square(i)`。这个调用与定义是一致的(在它之后),但与声明(在它之前)是不一致的。
C是设计为一次性处理的。构造的合法性取决于之前的代码,而不是之后的代码。
但是C标准规定的唯一要求是编译器必须发出至少一条诊断消息。它对该诊断消息的措辞没有任何规定,也不要求它是一个致命错误。在这种情况下,代码违反了一个约束(因为`square`的声明和定义是不兼容的),在我看来它应该拒绝这个程序,但一个警告也满足了标准的要求。
不管编译器的作者做出了什么决定,它确实产生了几条警告消息,你不应该忽视它们。它(也许是间接地)指出了你代码中的问题,你应该解决这些问题。编译器对你的代码发出警告有时比警告的实际内容更有信息性。
请复制并粘贴你问题中的确切代码并尝试编译它。如果它确实在没有警告或错误的情况下编译通过,那么你的编译器或你使用它的方式存在严重问题。
代码链接:[点击这里](https://onedrive.live.com/redir?resid=B9B6BE8E45151B68!26664&authkey=!AHSQBZtjT2UBZG8&v=3&ithint=photo%2cjpg)