【问题标题】:Two declarations of the same function in cc中同一函数的两个声明
【发布时间】:2014-01-22 14:31:51
【问题描述】:

我在测试中遇到了这个问题,但我仍然不明白给出的答案:

假设我写了以下代码:

#include <math.h>
#include <stdio.h>

float cos(float x){
    return 1-x*x/4;
}

int main()
{
    printf("%0f",cos(0.05f)+sin(0.05f));
}

假设cossin 在数学库中声明和定义(接收和返回double),我正在尝试将我的代码与数学库链接。

另一个假设是cos 是在math.c 中定义的。

问题是:

"代码会编译/链接成功吗?如果是,是哪个cos函数 会被调用吗?”

答案是:

“是的,代码会编译,我的cos会被调用”。

如何解释这种行为?这不是同一个函数的多个定义吗?

【问题讨论】:

  • 你是如何得到答案的""Yes, the code will compile and my cos will be called""
  • @Brandin 那是老师的解决方案。
  • @Brandin:它不依赖于实现,cos 是标准函数,C 中不允许重载。
  • 你的老师可能弄错了,打算使用double cos(double x)。在这种情况下,许多 C 实现将接受该程序,并且它将链接并运行,因为链接器从它提供的对象模块中获取 每个 模块,但只获取 需要的它提供的库中的模块。因此,因为cos 已经在程序中定义,链接器不会从数学库中获取它。然而,尽管这在许多 C 实现中都有效,但它违反了标准 C 的规则,即保留库标识符;普通程序可能不会定义它们。
  • @EricPostpischil:我会让你的评论成为答案。我认为你已经一针见血了。

标签: c


【解决方案1】:

您的老师可能犯了一个错误并打算使用double cos(double x)。在这种情况下,许多 C 实现将接受该程序,并且它将链接并运行,因为链接器从它提供的对象模块中获取 每个 模块,但只获取 需要的它提供的库中的模块。因此,因为cos 已经在程序中定义,链接器不会从数学库中获取它。然而,尽管这在许多 C 实现中都有效,但它违反了标准 C 的规则,该规则保留了库标识符;普通程序可能不会定义它们。

另一种可能是您的老师不打算包含math.h。这将使cos 的声明不会出错,因为它不会与另一个声明冲突,但它意味着sin 也应该由程序声明,因为它已被使用。

【讨论】:

  • 问题指出,在我们的(假设的)案例中,cos 是在 math.c 中定义的,而不是在 math.h 中。这会改变很多吗?
  • @Paz:这取决于程序的链接方式。如果将 math.c 编译为 math.o 并链接,我希望链接器抱怨cos 的多个定义。如果将 math.o 放入诸如 libm.a 之类的库中,然后进行链接,我希望链接器仅采用第一个定义而不是抱怨。在这种情况下,我认为更换老师可能是最好的选择。
  • 你后面的建议一定是他们的意思。
【解决方案2】:

它不会编译。

我在main() 的末尾添加了return 0; 以消除-Wall -Werror 的第二个问题。如果你这样做,你会看到:

$ gcc -Wall -Werror costest1.c -o costest -lm
costest1.c:5:1: error: conflicting types for ‘cos’

这在编译阶段失败,因为math.h 还定义了一个名为cos 的函数。注意cos 的原型是:

double cos(double x);

不是

float cos(float x);

如果你没有包含math.h,你可以编译,但是会得到:

$ gcc -Wall -Werror costest1.c -o costest -lm
costest1.c:5:1: error: conflicting types for built-in function ‘cos’ [-Werror]
costest1.c: In function ‘main’:
costest1.c:13:3: error: implicit declaration of function ‘sin’ [-Werror=implicit-function-declaration]
costest1.c:13:32: error: incompatible implicit declaration of built-in function ‘sin’ [-Werror]
cc1: all warnings being treated as errors

这是因为cos不是普通函数,而是作为builtin处理的。如您所见,它是根据sin 定义的。如果cos 是一个普通函数,您会看到某种重复符号错误。

在 C 中你不能有两个同名的函数,即使它们有不同的参数。在 C++ 中,您可以,因为相同名称的方法可能会因调用参数而异(但不仅仅是返回类型)。

【讨论】:

  • 使用 -Werror 不是对程序是否符合标准 C 的有效测试,因为它会改变编译器的行为以拒绝某些符合标准的程序。
  • 很公平。没有-Werror它会编译,但我相信这是因为cos是在math.h中定义的sin,所以库中没有实际的函数cos
  • 尤其是在main 中没有return 语句是完全有效的。 main 在这方面很特别。
  • 如果您不想返回值,我以为您应该将 main() 声明为 void
  • @abligh main() 函数必须始终定义为具有int 类型的返回值。
【解决方案3】:

Online C2011 standard:

6.7 声明
...
约束
...
4 在同一范围内引用同一对象或函数的所有声明应指定 兼容类型

您发布的代码违反了上述约束; math.h 声明 cos

double cos(double x);    

这种行为不能解释为C;它可以解释为 C++,它允许名称重载。

确保您真正谈论的是 C 而不是 C++,否则您最终会非常感到困惑。

【讨论】:

    【解决方案4】:

    编辑:

    我认为问题是关于 C++ 而不是 C,因为将代码编译为 C 程序会产生冲突类型错误:https://eval.in/93380

    此行为是由Function Overloading 引起的。 cos() 函数具有程序集名称 _cos@double 并重新声明 cos() 函数以接受 float 参数将具有程序集名称_cos@float 并且它不会与数学库中定义的 cos() 冲突.并且使用float 参数调用cos() 将被转换为对汇编函数_cos@float 的调用,这是您自己的cos()

    请注意,Function Overloading 只允许在 C++ 中使用,而不是 C。

    在 C 中你只能在不改变函数的参数和返回类型的情况下执行此操作 (https://eval.in/93381) 否则会产生前面的错误。

    正如假设所阐明的那样(cos 在 math.c 中定义),这种情况下的 cos() 函数不同于 math.h 中定义的函数,在这种情况下,如果它被定义为接受float 类型的参数并返回float 类型的值。在这种情况下,程序将毫无问题地编译。

    【讨论】:

    • 不幸的是它确实是 C 而不是 C++。
    • @Paz 这在 C 语言中不成立,因为您正在使用不同的参数类型重新定义 cos。这将始终产生冲突类型错误eval.in/93368
    • 在测试中明确提到了 cos() 的 math.h\math.c 版本是用 'double' 定义的。那么这种双重定义难免会出错呢?
    • @Paz 如果是这种情况,您的老师可能犯了错误。
    • @Paz:是的,这是违反约束的(请参阅下面的答案)。如果您的老师成功地编译该代码并按照描述运行它,那么他或她要么使用 C++ 编译器,要么使用严重不合格的 C 编译器。
    【解决方案5】:

    它根本不起作用。

    你会看到这样的错误:

    conflicting types for 'cos'
    

    我用 gcc 编译器的代码块得到它......

    您可以使用另一种解决方案,请查看此帖子:Override a function call in C

    【讨论】:

    • 如果cos 是一个宏定义,并且您在重新定义cos 之前调用了#undef cos 就可以了。我很确定 cos 不是宏。
    • @FiddlingBits 问题中没有提到,所以我假设情况并非如此。
    • 我很确定您的错误输出不是来自代码块,而是来自代码块调用的编译器。
    【解决方案6】:

    不,它不会编译。

    头文件math.h 声明double cos(double)。 C 中不允许尝试重载。

    error C2371: 'cos' : redefinition; different basic types
    

    【讨论】:

    • 至少在您的环境中。请参阅上面@Eric 的回答。
    • 在任何环境中。这必须引发编译错误。函数签名必须在 C 中唯一声明。Eric 并不反对这一点。
    猜你喜欢
    • 1970-01-01
    • 2012-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多