我的 Linux 开发项目的 Clang vs GCC

Posted

技术标签:

【中文标题】我的 Linux 开发项目的 Clang vs GCC【英文标题】:Clang vs GCC for my Linux Development project 【发布时间】:2012-01-02 14:24:44 【问题描述】:

我在上大学,我们在一个项目中使用 C。我们探索了 GCC 和 Clang,而 Clang 似乎比 GCC 更加用户友好。因此,我想知道在 Linux 上使用 C 和 C++ 进行开发时,使用 clang 而不是 GCC 有什么优点或缺点?

在我的情况下,这将用于学生级别的程序,而不是生产。

如果我使用 Clang,我应该使用 GDB 进行调试并使用 GNU Make,还是使用其他调试器和 make 实用程序?

【问题讨论】:

据我所知,Clang 还远未“成熟”,尤其是在标准库支持方面。尽管如此,它还是有非常棒的错误消息,所以你总是可以通过在 Clang 上尝试代码来解决一个神秘的编译器错误。我相信 Clang 也可以将 C++ 编译为 C。 @KerrekSB:clang 中缺少“标准库支持”的哪些元素? @StephenCanon:上次尝试时,我不得不使用 libstdc++(据我了解,这不是 Clang 的一部分)。就在前几天我们有this issue。无论如何,我没有追随最前沿,所以我的观点可能已经完全过时了。 @KerrekSB:关于您的链接,Clang 不适用于纯 Windows。它虽然在 MinGW 中工作。关于标准库,目前 Clang 还没有真正的标准库部分。 Clang 在 OSX 上与 libc++ 捆绑在一起,但是 libc++ 并未完全移植到其他环境中,因此那些 Clang 需要安装另一个标准库实现。在 Linux 上,libstdc++ 可以工作。 @KerrekSB:100% 支持 C++98。主要支持 C++11(最后我检查,不支持 <atomic>,可能还缺少其他一些小东西......我不能使用它,所以我没有完全跟上它的速度)。 【参考方案1】:

编辑:

gcc 的家伙确实提高了 gcc 的诊断体验(啊比赛)。他们创建了一个 wiki 页面来展示它here。 gcc 4.8 现在也有很好的诊断功能(gcc 4.9x 添加了颜色支持)。 Clang 仍处于领先地位,但差距正在缩小。


原文:

对于学生,我会无条件推荐 Clang。

gcc 和 Clang 在生成代码方面的表现现在还不清楚(虽然我认为 gcc 4.7 仍然领先,但我还没有看到确定的基准测试),但对于学生来说,学习这并不重要.

另一方面,Clang 极其清晰的诊断对于初学者来说肯定更容易理解。

考虑这个简单的 sn-p:

#include <string>
#include <iostream>

struct Student 
std::string surname;
std::string givenname;


std::ostream& operator<<(std::ostream& out, Student const& s) 
  return out << "" << s.surname << ", " << s.givenname << "";


int main() 
  Student me =  "Doe", "John" ;
  std::cout << me << "\n";

您会立即注意到Student 类的定义后缺少分号,对吧:)?

好吧,gcc notices it too,经过一段时间:

prog.cpp:9: error: expected initializer before ‘&’ token
prog.cpp: In function ‘int main()’:
prog.cpp:15: error: no match for ‘operator<<’ in ‘std::cout << me’
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:112: note: candidates are: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>& (*)(std::basic_ostream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:121: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ios<_CharT, _Traits>& (*)(std::basic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:131: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::ios_base& (*)(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:169: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:173: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:177: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(bool) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:97: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:184: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:111: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:195: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:204: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long long int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:208: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long long unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:213: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(double) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:217: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(float) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:225: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long double) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:229: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(const void*) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:125: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_streambuf<_CharT, _Traits>*) [with _CharT = char, _Traits = std::char_traits<char>]

Clang 也不完全是这里的主演,但仍然是:

/tmp/webcompile/_25327_1.cc:9:6: error: redefinition of 'ostream' as different kind of symbol
std::ostream& operator<<(std::ostream& out, Student const& s) 
     ^
In file included from /tmp/webcompile/_25327_1.cc:1:
In file included from /usr/include/c++/4.3/string:49:
In file included from /usr/include/c++/4.3/bits/localefwd.h:47:
/usr/include/c++/4.3/iosfwd:134:33: note: previous definition is here
  typedef basic_ostream<char>           ostream;        ///< @isiosfwd
                                        ^
/tmp/webcompile/_25327_1.cc:9:13: error: expected ';' after top level declarator
std::ostream& operator<<(std::ostream& out, Student const& s) 
            ^
            ;
2 errors generated.

我特意选择了一个触发不明确错误消息的示例(来自语法中的歧义),而不是典型的“哦,我的上帝 Clang 读懂了我的想法”示例。尽管如此,我们注意到 Clang 避免了错误的泛滥。无需吓跑学生。

【讨论】:

嗯...上次我检查时,我阅读了一篇发布各种基准测试的文章,其中在任何测试中,clang 几乎都将 gcc 从水中吹了出来。来源:clang.llvm.org/features.html#performance @AscensionSystems:请注意,这些测试显示的是 Clang 二进制文件本身的性能(那是不久前),而不是您正在编译的二进制文件的性能。 这是一个很好的观点,我很想看看编译后的可执行文件之间的比较。我的印象是 clang 在优化方面做得更好,但我实际上没有看到任何基准。我去看看。 @AscensionSystems:这是我知道比较gcc 4.6 to llvm 3.0 的最新板凳,它显示了 gcc 的平均净优势。同样有趣的可能是DragonEgg bench,DragonEgg 是一个插件,它允许使用 gcc 前端(可能还有优化器)然后使用 LLVM 后端来生成代码。 上次我检查时,phoronix 基准非常不可信:编译器标志没有正确记录,但结果表明事情没有正确设置。【参考方案2】:

截至目前,GCC 对 C++11 功能的支持比 Clang 更好、更完整。此外,GCC 的代码生成器比 Clang 中的代码生成器执行更好的优化(根据我的经验,我没有看到任何详尽的测试)。

另一方面,Clang 通常比 GCC 更快地编译代码,并且在您的代码出现问题时产生更好的错误消息。

选择使用哪一个实际上取决于对您来说重要的事情。我更看重 C++11 支持和代码生成质量,而不是编译的便利性。因此,我使用 GCC。对您而言,取舍可能会有所不同。

【讨论】:

这是最新的 Phoronix 文章比较 GCC 4.6 vs Clang 3.0 以及特定于推土机平台的 previous article。根据基准,获胜者要么是一个,要么是另一个(在上一篇文章中,gcc 4.7 也出现了),所以我个人觉得不清楚哪个表现更好。 为什么不同时使用呢? Clang 用于开发,GCC 用于生产。 @segfault:这就是我目前正在做的事情。这个答案已经很老了,而且不再完全正确。自从我编写 Clang 和 GCC 以来,它都得到了显着改进(特别是,Clang 现在匹配 GCC 的整体 C++11 支持,并且 GCC 改进了它的错误消息和编译速度)。现在我建议同时使用两者,稍微偏爱 Clang,因为 Clang 源代码比 GCC 源代码更容易理解。【参考方案3】:

我同时使用这两种方法是因为有时它们会给出不同的有用错误消息。

当一位核心开发人员第一次尝试使用 clang 进行编译时,Python 项目能够找到并修复一些小 bug。

【讨论】:

您对使用 clang 进行调试构建而使用 gcc 进行优化发布有何看法? 使用 Clang 开发并使用 GCC 发布是合理的,但请确保您的 GCC 版本通过了您的测试套件(包括和不包括 NDEBUG)。 感谢您的回复。我已经尝试了一段时间,效果非常好。我也收到了不同的警告,这很棒。【参考方案4】:

我同时使用 Clang 和 GCC,我发现 Clang 有一些有用的警告,但对于我自己的光线追踪基准测试 - 它始终比 GCC 慢 5-15% (当然,请注意这一点,但是尝试对两者使用类似的优化标志)。

所以现在我使用 Clang 静态分析及其带有复杂宏的警告:(尽管现在 GCC 的警告几乎一样好 - gcc4.8 - 4.9)。

一些注意事项:

Clang 不支持 OpenMP,只有当你利用它时才重要,但既然我这样做了,它对我来说是一个限制。 (*****) 可能不太支持交叉编译(例如 FreeBSD 10 仍然使用 GCC4.x 用于 ARM),例如 gcc-mingw 在 Linux 上可用... (YMMV)。 一些 IDE 还不支持解析 Clangs 输出(例如QtCreator *****)。 编辑:QtCreator 现在支持 Clang 的输出 GCC 的某些方面得到了更好的记录,而且由于 GCC 的存在时间更长并且被广泛使用,您可能会发现更容易获得有关警告/错误消息的帮助。

***** - 这些领域正在积极开发中,可能很快就会得到支持

【讨论】:

我也使用 OpenMP,但我正在考虑切换到 TBB,我猜它可以与 Clang 一起使用。 TBB 在某些情况下可能是 OpenMP 的可行替代方案(但据我所知,仅适用于 C++),对于 C 不支持 - 也适用于大型项目,从 OpenMP 切换到其他东西可能不值得,尤其是如果 Clang 最终将支持 OpenMP。【参考方案5】:

对于学生级别的程序,Clang 的好处是默认情况下更严格。 C标准。例如,以下 K&R 版本的 Hello World 被 GCC 接受而没有警告,但被 Clang 拒绝并带有一些描述性很强的错误消息:

main()

    puts("Hello, world!");

使用 GCC,你必须给它-Werror 才能让它真正表明这不是一个有效的 C89 程序。另外,您仍然需要使用c99gcc -std=c99 来获取C99 语言。

【讨论】:

gcc 通常至少应使用-Wall 调用,这确实会对此程序发出警告。不过,clang 确实会产生很好的警告/错误。 @caf:这正是我想要表达的观点,对于 GCC,您必须传递选项。开箱即用,它可能对教学目的过于宽容。 这可能是真的,但这是一个相当次要的问题。更重要的是错误消息的质量。 GCC 4.6 已经相当不错了,虽然我知道 clang 在那里做了一些真正的魔法。 @dreamlax:是的;还有gnu99gnu++98gnu++0x。不过,我认为这些是真正的扩展,也就是说,它们可以顺利编译符合 ISO 标准的代码。以下是详细信息:for C、for C++。 此程序不应产生错误或警告。符合标准。【参考方案6】:

我认为 clang 可能是一个替代方案。

GCC 和 clang 在 a+++++a 这样的表达上存在一些差异,我和我在 Mac 上使用 clang 而我使用 gcc 的同行有很多不同的答案。

GCC 已成为标准,而 clang 可能是替代方案。因为GCC非常稳定,clang还在开发中。

【讨论】:

Clang 正在迅速准备在 Linux 世界中完全取代 GCC,并且在 BSD 世界中已大体如此。它在几年前取代了 Mac 上的 GCC。 Clang是个好东西。我认为 GCC 可以成为个人的替代方案,对此我很高兴。 表达式 a+++++a 未定义,因此期望在每个编译器上得到不同的答案,甚至在同一编译器的不同版本上。在不同时间编译时,您甚至可以在同一编译器上为该表达式获得不同的结果。这就是“未定义”的意思。 a+++++a 应该失败,因为它被解析为 a ++ ++ + a,这是一个语法错误。 @Lelanthran 这不是 undefined 的意思。它具有未定义的行为,因此编译器可能无法编译它,或者它可能会在运行时抛出或锁定 CPU,因此您需要进行硬重置或更险恶的事情。

以上是关于我的 Linux 开发项目的 Clang vs GCC的主要内容,如果未能解决你的问题,请参考以下文章

g++ vs intel/clang 参数传递顺序?

自动模板参数:g ++ 7.3 vs clang ++ 6.0:哪个编译器正确?

Windows下的Mingw + clang vs Boost:不同类型的错误

vs2019 编译项目报错:MSB6006 “clang.exe”已退出,代码为 5 Microsoft.Cpp.Clang.targets 155 ClangCompile

vs2019 编译项目报错:MSB6006 “clang.exe”已退出,代码为 5 Microsoft.Cpp.Clang.targets 155 ClangCompile

在Windows使用clang编译器