5

使用以下代码:

int main(){
    printf("%f\n",multiply(2));
    return 0;
}

float multiply(float n){
    return n * 2;
}

当我尝试编译时,我收到一个警告:“'%f' 期望 'double',但参数的类型为 'int'”和两个错误:“'multiply' 的类型冲突”,“'multiply' 的先前隐式声明是这里。”

问题1:我猜这是因为编译器在第一次遇到“乘法”函数时不知道它,他会发明一个原型,并且发明的原型总是假设“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。同样,对幕后发生的事情有什么解释吗?

提前非常感谢。

4

4 回答 4

4

因此,发明的原型将是“int multiply(int)”,因此会出现错误。它是否正确?

绝对地。这样做是为了与缺少函数原型的 pre-ANSI C 向后兼容,并且没有类型声明的所有内容都是隐式的int。编译器编译你的main,创建一个隐含的定义int multiply(int),但是当它找到真正的定义时,它会发现谎言,并告诉你它。

当我将代码分解为它编译的 2 个文件时,怎么会这样?

编译器永远不会发现原型的谎言,因为它一次编译一个文件:它假设multiply接受一个int,并int在你mainmultiply.c. 但是,运行此程序会产生未定义的行为。

一旦我运行上面的程序(版本分为 2 个文件),结果是 0.0000 打印在屏幕上。

这是上述未定义行为的结果。该程序将编译和链接,但因为编译器认为multiply需要一个int,它永远不会转换22.0Fmultiply也永远不会发现。int 同样,通过将重新解释为函数float内部的a加倍计算的不正确值multiply将被视为int再次。

于 2013-04-18T13:56:07.950 回答
1

问题1:是的,你是对的。如果没有函数原型,则默认类型为int

问题 2:当您将此代码编译为一个文件时,编译器会看到已经有一个名为的函数multiply,并且它的类型与假设的不同(double而不是int)。因此编译不起作用。

当您将其分成两个文件时,编译器会生成两个.o文件。在第一个中,它假设该multiply()函数将在其他文件中。然后链接器将这两个文件链接到一个二进制文件中,并根据名称在第一个文件中编译器假定的位置multiply插入调用。float multiply()int multiply().o

问题 3:如果你读int 2为 a float,你会得到一个非常小的数字(~1/2^25),所以之后你将它乘以 2 ,它对于 format 来说仍然太小了%f。这就是你看到的原因0.00000

于 2013-04-18T13:55:20.760 回答
1

未指定函数的返回类型为int(这就是您收到警告的原因,编译器认为它返回一个整数)和未知数量的未指定参数。

如果您将项目分解为多个文件,只需在从其他文件调用函数之前声明一个函数原型,一切都会正常工作。

于 2013-04-18T13:55:26.730 回答
1

问题1:

因此,发明的原型将是“int multiply(int)”,因此会出现错误。它是否正确?

不完全是,因为它取决于您的 Cx (C89, C90, C99,...)

对于函数返回值,在 C99 之前明确规定,如果没有可见的函数声明,则翻译器提供一个。这些隐式声明默认返回类型为 int

来自C 标准的理由(6.2.5 第 506 页)

在 C90 之前,没有函数原型。开发人员希望能够交换具有相同整数类型的有符号和无符号版本的参数。如果函数定义中的参数类型具有不同的符号,则必须强制转换参数,这被视为与 C 的简单类型检查系统背道而驰,并且有点侵入性。原型的引入并没有完全消除论点可互换性的问题。省略号表示对 1590 省略号一无所知,不提供预期参数类型的信息。类似地,对于函数返回值,在 C99 之前明确规定,如果没有可见的函数声明,则翻译器提供一个。这些隐式声明默认返回类型为 int 。如果实际函数碰巧返回类型 unsigned int ,那么这样的默认声明可能会返回意外结果。许多开发人员对函数声明的态度很随意。我们其他人不得不忍受委员会不想破坏他们编写的所有源代码的后果。函数返回值的可互换性现在是一个争论点,因为 C99 要求函数声明在调用点可见(不再提供默认声明)

问题2:

当我将代码分解为它编译的 2 个文件时,怎么会这样?

它会编译,并且会像第一个问题中指出的那样被对待,完全一样

于 2013-04-18T13:55:42.457 回答