关于C函数原型和编译的问题
使用以下代码:
int main(){ printf("%f\n",multiply(2)); return 0; } float multiply(float n){ return n * 2; }
当我尝试编译时,我得到一个警告:“’%f’期望’double’,但是参数的类型为’int’”和两个错误:“’multiply’的冲突类型”,“先前隐式声明’multiply’是这里。”
问题1 :我猜这是因为,鉴于编译器在第一次碰到它时不知道函数’multiply’,他将发明一个原型,发明原型总是假设’int’都被返回并被视为参数。 因此,本发明的原型将是“int multiply(int)”,因此也就是错误。 它是否正确?
现在,以前的代码甚至都不会编译。 但是,如果我打破两个文件中的代码,如下所示:
#file1.c int main(){ printf("%f\n",multiply(2)); return 0; } #file2.c float multiply(float n){ return n * 2; }
并执行“gcc file1.c file2.c -o file”它仍然会发出一个警告(printf期望是double但是正在获取int)但是错误将不再显示并且它将被编译。
问题2 :为什么我将代码分成2个文件进行编译?
问题3 :一旦我运行上面的程序(版本分成2个文件),结果就是在屏幕上打印了0.0000。 怎么会? 我猜测编译器再次发明了一个与函数不匹配的原型,但为什么会打印0? 如果我将printf(“%f”)更改为printf(“%d”),它会打印1.再次,对幕后发生的事情的任何解释?
非常感谢提前。
因此,本发明的原型将是“int multiply(int)”,因此也就是错误。 它是否正确?
绝对。 这样做是为了向后兼容缺少函数原型的前ANSI C,并且没有类型声明的所有内容都是隐式int
。 编译器编译你的main
,创建一个int multiply(int)
的隐式定义,但是当它找到真正的定义时,它会发现谎言并告诉你它。
为什么我将代码分成2个文件进行编译?
编译器永远不会发现关于原型的谎言,因为它一次编译一个文件:它假设multiply
接受一个int
,并在main
返回一个int
,并且在multiply.c
找不到任何矛盾。 但是,运行此程序会产生未定义的行为。
一旦我运行上面的程序(版本分成2个文件),结果是在屏幕上打印0.0000。
这是上述未定义行为的结果。 程序将编译和链接,但因为编译器认为multiply
采用int
,它永远不会将2
转换为2.0F
,而2.0F
将永远不会找到。 类似地,通过将在您的multiply
函数内重新解释为float
的int
加倍计算的错误值将再次视为int
。
未指定的函数的返回类型为int
(这就是为什么你得到警告,编译器认为它返回一个整数)和未知数量的未指定参数。
如果你在多个文件中分解你的项目,只需在从其他文件调用函数之前声明一个函数原型,一切都会正常工作。
问题1:
因此,本发明的原型将是“int multiply(int)”,因此也就是错误。 它是否正确?
不是exactelly是因为它取决于你的Cx(C89,C90,C99,……)
对于函数返回值,在C99之前,明确规定如果没有可见的函数声明,则转换器提供一个。 这些隐式声明默认为返回类型int
C标准的理由(6.2.5第506页)
在C90之前,没有function原型。 开发人员希望能够交换具有相同整数类型的有符号和无符号版本的论证。 必须抛出一个参数,如果函数定义中的参数类型具有不同的符号,则被视为与C的易于进行的类型检查系统相反并且有点干扰。 原型的引入并没有完全消除论证的可互换性问题。 省略号表示没有任何关于1590省略号的信息,没有信息预期类型的参数。 类似地,对于函数返回值,在C99之前,明确规定如果没有可见的函数声明,则转换器提供一个。 这些隐式声明默认为返回类型int。 如果实际函数碰巧返回unsigned int类型,则这样的默认声明可能返回了意外结果。 许多开发人员对function声明持休闲态度。 我们其他人不得不忍受委员会不想破坏他们所写的所有源代码的后果。 函数返回值的可互换性现在是一个没有实际意义的点,因为C99要求在调用点处显示函数声明(不再提供默认声明)
问题2:
为什么我将代码分成2个文件进行编译?
它会编译,它将被视为在第一个问题中表示为exactelly相同
问题1:是的,你是对的。 如果没有函数原型,则默认类型为int
问题2:当您将此代码编译为一个文件时,编译器会看到已存在名为multiply
函数,并且它具有与假设不同的类型( double
而不是int
)。 因此编译不起作用。
当您将它分成两个文件时,编译器会生成两个.o
文件。 在第一个中,它假设multiply()
函数将在其他文件中。 然后链接器将两个文件链接到一个二进制文件,并根据名称multiply
在第一个.o
文件中编译器假定的int multiply()
的位置插入float multiply()
调用。
问题3:如果你把int
2
读成一个float
,你会得到一个非常小的数字(~1 / 2 ^ 25),所以在那之后你将它乘以2并且对于格式%f
它仍然太小。 这就是你看到0.00000
的原因。