为什么我没有“未定义的引用”?[复制]

Why don't I have "undefined reference to"? [duplicate]

提问人:n0p 提问时间:9/5/2016 最后编辑:Communityn0p 更新时间:9/5/2016 访问量:1844

问:

我在我的(大)项目中遇到了类似的问题

//# include <string.h> // not included

void foo(char * str, const char * delim)
{
    char * tok = strtok(str, delim); 
    // warning ^ "assignement makes pointer from integer without a cast"

    // [...]
}

答案(只需添加即可拥有原型)确实解决了这个问题。#include <string.h>strtok

但是,由于我对编译器/链接器的了解不足,我无法理解该过程如何接受尚未原型化的函数。 我宁愿期待错误,这是当您忘记包含正确的标题时的典型错误。undefined reference to function 'strtok'

[编辑]我理解为什么这个问题被标记为重复,但我确实认为它是不同的:我知道关于包含的“良好做法”,我只是想知道编译器的行为。但是,我承认我可以在这篇文章中找到(部分)我问题的答案:C89、C90 或 C99 中的所有功能都需要原型吗? 或者这个: 必须在 C 中声明函数原型?

c gcc 编译 链接器 undefined-reference

评论

1赞 Klas Lindbäck 9/5/2016
简短的回答:如果将代码编译为 C99 或更高版本,则会出现错误。添加 gcc 标志或 .-std=c11-std=c99
0赞 too honest for this site 9/5/2016
编译器永远不会“抛出”(无论这意味着什么)或报告这样的错误。这就是链接器。无论“必须”是什么:您绝对应该这样做。过去 20 年中任何半途而废的现代编码标准都需要这样做。
0赞 n0p 9/5/2016
@Olaf已修复(这实际上已经是关于我帖子答案的线索)。有没有机会在我的编辑后恢复“重复”标记?
0赞 too honest for this site 9/5/2016
我不认为这不是骗局,所以我不会投票重新开放。如果你想更详细地了解这一点,只需阅读标准或一本好的 C 书(关于这个主题,只有一本旧的书可能会更好)。否则,只需启用警告,使用至少支持 C99 的现代 C 编译器,以便您收到警告,启用所有其他推荐警告,然后......使用原型!

答:

3赞 Sourav Ghosh 9/5/2016 #1

链接二进制文件时,除非明确提及,否则二进制文件无论如何都会与默认的 C 标准库(例如)链接,其中定义了函数。glibc

因此,当您错过包含声明的头文件时,您最终会收到警告(如果函数原型有不匹配的注释),但在链接期间,由于默认 C 库的存在,程序无论如何都会成功链接。

FWIW,根据 ,对隐式函数声明的支持已被删除,但大多数编译器都支持不良行为,以保持遗留代码的向后兼容性。C11


注意:

隐式函数声明:早些时候,(在 C99 之前,AFAIK),该标准没有强制要求函数具有正向声明。如果一个函数在没有正向声明的情况下使用,则假定它返回并接受任意数量的传入参数。int

评论

0赞 Andrew Henle 9/5/2016
我会说“不良行为”有点强烈——这是不同的行为。对于遗留代码,不需要原型是一项关键的语言功能,因为只为没有原型的函数添加原型是不安全的,因为函数参数的处理方式不同 - 参见 stackoverflow.com/questions/1255775/...
0赞 Sourav Ghosh 9/5/2016
@AndrewHenle 对不起,但我没有明白重点。难道没有可变参数函数可以在符合标准的同时实现相同的功能吗?我错过了什么?
1赞 P.P 9/5/2016 #2

因为 gcc 会自动将您的代码与 C 库链接起来。“未定义的函数引用”错误通常由链接器在无法解析符号时发出,并且仅当在任何链接的库中找不到该符号时才会发生(链接的顺序可能也很重要)。但是 C 库是默认链接的——就好像你用 .所以,你不会得到那个错误。-lc

如果你告诉 gcc 不要使用 链接 C 库,那么你会看到你预期的错误:-nostdlib

$ gcc -nostdlib file.c

另一方面,您应该始终为函数提供原型。

您可能对其他类似的链接器选项感兴趣,例如 等,您可以在 gcc 的手册中找到这些选项。-nodefaultlibsnostartfiles