您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

技术分享——gcc、clang、msvc等编译器的区别

时间:2021-03-22 10:48:14  来源:  作者:

仅就从性能部分的错误诊断比较区别,之前见国外有相关文章,我做简单分享,但是,如果要了解区别,无妨从下文中包含的gcc和msvc阅读了解一下。

GCC和Clang一直在彼此较劲尝试证明谁的错误诊断更好。Clang首先在他们的“表现诊断”文章中讨论过GCC :

相关链接:
https://clang.llvm.org/diagnostics.html

彼方唱罢我登场,而后在GCC改进了自己的诊断程序后,又发布了就其性能提升后的文章,标题是“Comparison of Diagnostics between GCC and Clang”,可以看到非常直白和露骨了......

相关链接:
https://gcc.gnu.org/wiki/ClangDiagnosticsComparison

我们可以通过测试Clang 6.0.0、GCC 7.3.0中常见错误,同时通过编译器资源管理器MSVC 2017 19.10.25107来检验谁的性能更好。需要特别说明的是GCC 8似乎已改进了某些消息,但它仍旧不能算是一个稳定的版本。

我个人是把MSVC和Clang中的静态分析器排除在外的,因为将他们中的分析器拿来与GCC这方面的缺失做相应的比较,肯定是不公平的,由此,仅使用-Wall或/ W3,除非没有发现错误,否则我将尝试-Weverything,-Wextra -Wpedantic或/ Wall。

 

第一轮:关于“分号”的缺失

忘记分号,一直都是这样的

semicolon.c

#include <stdio.h>

int main(void) {
    printf("Hello, world!n") // no semicolon
    return 0 // no semicolon
}

这是在printf语句和return语句之后的惯常会丢失的分号,类似这样一个常见的错误,应该开始意识到这个问题了吧,再如

~ $ gcc-7 -Wall semicolon.c 
semicolon.c: In function 'main':
semicolon.c:5:5: error: expected ';' before 'return'
     return 0 // no semicolon        
     ^~~~~~

 

C:> cl /W3 /diagnostics:caret semicolon.c
semicolon.c(5,5): error C2143: syntax error: missing ';' before 'return'
    return 0 // no semicolon
    ^
semicolon.c(6,1): error C2143: syntax error: missing ';' before '}'
}
^
Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25017 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

没错吧,

~ $ clang-6.0 -Wall semicolon.c
semicolon.c:4:30: error: expected ';' after expression
    printf("Hello, world!n") // no semicolon
			     ^
			     ;
semicolon.c:5:13: error: expected ';' after return statement
    return 0 // no semicolon
	    ^
	    ;
2 errors generated.

Clang显然是赢家,其次是MSVC。而GCC没法识别第二个错误。MSVC和GCC会僵硬的出现把 ';' 放在 'return'"之前的 errors,技术上这来讲,说得通,但不太聪明。

第一轮打分:Clang: 2, GCC: 0, MSVC: 1

 

第二轮:缺花括号

在函数末尾丢失花括号是另一个常见错误,尽管不如前一个常见。

missingbrace.c

int main(void) {
    return 0;
// no closing brace

希望GCC或MSVC能够把这个问题解决了,

~ $ gcc-7 -Wall missingbrace.c
missingbrace.c: In function 'main':
missingbrace.c:2:5: error: expected declaration or statement at end of input
     return 0;
     ^~~~~~

实在糟糕透顶,

C:> cl /W3 /diagnostics:caret missingbrace.c
missingbrace.c(1): fatal error C1075: the left brace '{' was unmatched at the end of the file
int main(void) {
Internal Compiler Error in Z:optcompiler-explorerwindows19.10.25017libnativebinamd64_x86cl.exe.  You will be prompted to send an error report to Microsoft later.
INTERNAL COMPILER ERROR in 'Z:optcompiler-explorerwindows19.10.25017libnativebinamd64_x86cl.exe'
    Please choose the Technical Support command on the Visual C++
    Help menu, or open the Technical Support help file for more information
Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25017 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

我认为我有可能搞错了, 但是看上去MSVC崩掉了。Microsoft系统可以的,除了崩掉MSVC再没缺点。

~ $ clang-6.0 -Wall missingbrace.c
missingbrace.c:2:14: error: expected '}'
    return 0;
	     ^
missingbrace.c:1:16: note: to match this '{'
int main(void) {
	       ^
1 error generated.

仍旧,Clang收入2分.

1-2轮积分情况: Clang: 4,GCC: 0,MSVC: 2

 

第三轮:out of bounds

又一个非常常见的错误,

outofbounds.c
#include <stdio.h>

static const int array[4] = { 1, 2, 3, 4 };

int main(void) {
    for (int i = 0; i <= 4 /* should be < */; i++) {
        printf("%d ", array[i]);
    }
    return 0;
}

颇为有趣的是, 即使用 -Warray-bounds 或者 /Wall,Clang、MSVC都未曾搞清这点。

但是,在使用-O2时,GCC实际上呈现出的是进行更改的正确选择!

~ $ gcc-7 -Wall -O2 outofbounds.c
outofbounds.c: In function 'main':                    
outofbounds.c:7:9: warning: iteration 4 invokes undefined behavior [-Waggressive-loop-optimizations]
        printf("%d ", array[i]);
        ^~~~~~~~~~~~~~~~~~~~~~~
outofbounds.c:6:5: note: within this loop
    for (int i = 0; i <= 4 /* should be < */; i++) {
    ^~~

不过,GCC在这里只得到一点,因为它并不总是显示此错误。

1-3轮积分情况: clang:4,GCC:1,MSVC:2

 

第四轮: ifs 无花括号

ifs没有花括号,尽管它们显得很方便,但带来的危害往往大于弊端,例如恶名远扬的goto失败漏洞:

相关链接:
https://nakedsecurity.sophos.com/2014/02/24/anatomy-of-a-goto-fail-Apples-ssl-bug-explained-plus-an-unofficial-patch/

if-else-bug.c

#include <stdio.h>

int main(int argc, char**argv) {
    if (argc > 1) // needs braces
        argc--;
        argv++;
    else
        printf("Usage: %s <arguments>n", *argv); // (this would theoretically be UB because of the argv++)
    return 0;
}

自然地作为Apple的编译器,Clang是应该意识到这个错误的,

~ $ clang-6.0 -Wall if-else-bug.c
if-else-bug.c:8:5: error: expected expression
    else
    ^
1 error generated.

……这是一个非常无用的错误,也难怪苹果没有发现。

C:> cl /W3 /diagnostics:caret if-else-bug.c
(7): error C2181: illegal else without matching if
    else
    ^
Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25017 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

MSVC呈现的至少是有意义的,完全不像clang搞出些垃圾来。

~ $ gcc-7 -Wall if-else-bug.c
if-else-bug.c: In function 'main':
if-else-bug.c:5:5: warning: this 'if' clause does not guard... [-Wmisleading-indentation]
     if (argc > 1) // needs braces
     ^~
if-else-bug.c:7:9: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'
         argv++;
         ^~~~
if-else-bug.c:8:5: error: 'else' without a previous 'if'
     else
     ^~~~

没想到,GCC有次一次鹤立鸡群了下,

1-4轮积分情况: Clang: 4,GCC: 3,MSVC: 2

 

第五轮:JAVA风格的字符串连接

Java、JavaScript、C ++(一定程度上)和其他几种语言使你可以使用'+'来连接字符串和其他内容。C并没有达到你的期望。

字符串concat.c

#include <stdio.h>

int main(void) {
    int value = 4;
    const char *string = "value = " + value; // This isn't Java!
    printf("%sn", string);
    return 0;
}

 

~ $ gcc-7 -Wall -Wextra -pedantic string-concat.c
~ $ clang-6.0 -Wall string-concat.c
string-concat.c:5:37: warning: adding 'int' to a string does not append to the string [-Wstring-plus-int]
    const char *string = "value = " + value; // This isn't Java!
                         ~~~~~~~~~~~^~~~~~~
string-concat.c:5:37: note: use array indexing to silence this warning
    const char *string = "value = " + value; // This isn't Java!
                                    ^
                         &          [      ]
1 warning generated.

GCC和MSVC 在这件事上完全没搞定,而clang给出了一个有用的报错.

1-5轮积分情况:Clang: 6,GCC: 3, MSVC: 2

 

第六轮:忘记返回值

有时,你忘记了一个函数需要返回一个值,或者忘记在这个switch语句之后放置一个return语句,或者还有其它种种,

no-return.c

no-return.c

#include <stdlib.h>

int doesNotReturnAValue(void) {
    // no return value
}

int mightNotReturnAValue(void) {
    if (rand() % 2 == 0) {
        return 2;
    }
    // if rand() is odd, there is no return value
}

 

~ $ gcc-7 -Wall no-return.c
no-return.c: In function 'doesNotReturnAValue':
no-return.c:5:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^
no-return.c: In function 'mightNotReturnAValue':
no-return.c:12:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^
~ $ clang-6.0 -Wall no-return.c
no-return.c:5:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
no-return.c:12:1: warning: control may reach end of non-void function [-Wreturn-type]
}
^
2 warnings generated.

啊,0分,完全没价值

C:> cl /W3 /diagnostics:caret no-return.c
no-return.c(5) : warning C4716: 'doesNotReturnAValue': must return a value
no-return.c(12) : warning C4715: 'mightNotReturnAValue': not all control paths return a value
Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25017 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

MSVC做到了。

1-6轮积分情况:Clang: 6,GCC: 3,MSVC: 4

 

第七轮:忘记命名空间

是时候来一些C ++了!

我经常犯的错误包括,忘记添加“使用命名空间”或是在调用之前放置命名空间。

no-namespace.cpp

#include <IOStream>

int main() {
    cout << "Hello, world!n"; // should be std::cout
    return 0;
}

让我们看看编辑器怎么呈现

C:> cl /W3 /diagnostics:caret no-namespace.cpp
no-namespace.cpp(4): error C2065: 'cout': undeclared identifier
    cout << "Hello, world!n"; // should be std::cout
    ^
Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25017 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

对于微软,这点上,没什么好感,

~ $ g++-7 -Wall no-namespace.cpp
no-namespace.cpp: In function 'int main()':
no-namespace.cpp:4:5: error: 'cout' was not declared in this scope
     cout << "Hello, world!n"; // should be std::cout
     ^~~~
no-namespace.cpp:4:5: note: suggested alternative:
In file included from no-namespace.cpp:1:0:
/usr/include/c++/7.3.0/iostream:61:18: note:   'std::cout'
   extern ostream cout;  /// Linked to standard output
                  ^~~~

这个看上去好些, 虽然返回的消息有点迷,但是GCC能够搞清楚我们指的是std::cout。再看看clang。

~ $ clang++-6.0 -Wall no-namespace.cpp
no-namespace.cpp:4:5: error: use of undeclared identifier 'cout'; did you mean 'std::cout'?
    cout << "Hello, world!n"; // should be std::cout
    ^~~~
    std::cout
/usr/include/c++/v1/iostream:54:33: note: 'std::cout' declared here
extern _LIBCPP_FUNC_VIS ostream cout;
                                ^
1 error generated.

好吧,对于clang来说,虽然和GCC显示了相同的信息,但还是和GCC有所区别的,clang能直接的问到点子上“你是否是指std::cout",而后才会去执行,clang两分,GCC得一分.

1-7轮积分情况: Clang: 8,GCC: 4,MSVC: 4

 

第八轮:dynamic_casting,一个类本身

C ++ dynamic_cast应该在指向类的指针上使用,而不是在类本身上使用,这是蛮奇怪的。

casting-a-class.cpp

class Base {};
class Derived : public Base {};

int main() {
    Base base;
    Derived derived = dynamic_cast<Derived>(base); // should be used on a pointer
    return 0;
}

 

~ $ clang++-6.0 -Wall casting-a-class.cpp
casting-a-class.cpp:6:23: error: 'Derived' is not a reference or pointer
    Derived derived = dynamic_cast<Derived>(base); // should be a pointer
                      ^           ~~~~~~~~~
1 error generated.

天呐,这块clang竟然报错,

~ $ g++-7 -Wall casting-a-class.cpp
casting-a-class.cpp: In function 'int main()':
casting-a-class.cpp:6:49: error: cannot dynamic_cast 'base' (of type 'class Base') to type 'class Derived' (target is not pointer or reference)
     Derived derived = dynamic_cast<Derived>(base); // should be a pointer
                                                 ^

这点上看,GCC更清晰一些,虽然我不知道它指向的是什么。

C:> cl /W3 /diagnostics:caret casting-a-class.cpp
casting-a-class.cpp(6,49): error C2680: 'Derived': invalid target type for dynamic_cast
    Derived derived = dynamic_cast<Derived>(base); // should be used on a pointer
                                                ^
casting-a-class.cpp(6,49): note: target type must be a pointer or reference to a defined class
    Derived derived = dynamic_cast<Derived>(base); // should be used on a pointer
                                                ^
Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25017 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

MSVC在上面这里胜出。虽然我感觉它有指向性的错误,但至少把这个问题正确报答出来了。

8轮下来总积分:Clang: 8,GCC: 5,MSVC: 6

特别补充说明,前面完全没有要说其中哪个很差劲,三者无疑各有优缺点, 但是,仅就诊断功能上来说clang被证明更强大些。

 

文章来源:
https://easyaspi314.github.io/gcc-vs-clang.html



Tags:编译器   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
C语言作为高级语言,用其编写的程序指令机器并不能识别,因此需要有一个编译器将其转换为机器可识别的二进制指令。C语言已经出现50多年的时间,其相关的编译器种类众多,从最早先的...【详细内容】
2021-08-24  Tags: 编译器  点击:(130)  评论:(0)  加入收藏
仅就从性能部分的错误诊断比较区别,之前见国外有相关文章,我做简单分享,但是,如果要了解区别,无妨从下文中包含的gcc和msvc阅读了解一下。GCC和Clang一直在彼此较劲尝试证明谁的...【详细内容】
2021-03-22  Tags: 编译器  点击:(617)  评论:(0)  加入收藏
编译器一般构成传统的编译器通常分为三个部分,前端(frontEnd),优化器(Optimizer)和后端(backEnd)。在编译过程中,前端主要负责词法和语法分析,将源代码转化为抽象语法树;优化器则...【详细内容】
2020-11-03  Tags: 编译器  点击:(109)  评论:(0)  加入收藏
gcc编译器编译过程详解gcc常用选项 一个c/c++文件要经过预处理、编译、汇编和链接才能变成可执行文件。 (1)预处理 C/C++源文件中,以#开头的命令被称为预处理命令,如包含命令#in...【详细内容】
2020-07-30  Tags: 编译器  点击:(51)  评论:(0)  加入收藏
简单介绍一下编程方式的历史演变。-- Erik O&#39;shaughnessy(作者)在计算机诞生不久的早期年代,硬件非常昂贵,而程序员比较廉价。这些廉价程序员甚至都没有“程序员”这个头衔...【详细内容】
2020-06-26  Tags: 编译器  点击:(70)  评论:(0)  加入收藏
原作:Archie Mistry翻译:豌豆花下猫@Python猫原文:https://morioh.com/p/765b19f066a4Python 是一门对初学者友好的编程语言,是一种多用途的、解释性的和面向对象的高级语言。它...【详细内容】
2020-01-10  Tags: 编译器  点击:(27)  评论:(0)  加入收藏
各位,关于编译器和集成开发环境这两个名称,我们平时一直在说,但这二位究竟有什么区别和联系呢,今天就跟大家简单聊一聊。 我们平时所说的程序,是指双击后就可以直接运行的程序,这...【详细内容】
2019-11-11  Tags: 编译器  点击:(102)  评论:(0)  加入收藏
我们平时所说的程序,是指双击后就可以直接运行的程序,这样的程序被称为可执行程序(Executable Program)。在 Windows 下,可执行程序的后缀有 .exe 和 .com(其中 .exe 比较常见);在类...【详细内容】
2019-10-12  Tags: 编译器  点击:(135)  评论:(0)  加入收藏
华为方舟编译器终于正式开源,源代码放出,兑现了在8月开源的承诺。...【详细内容】
2019-09-02  Tags: 编译器  点击:(356)  评论:(0)  加入收藏
今日华为EMUI官方微信发文宣布,方舟编译器开源准备就绪,将于8月31日起开源。方舟编译器是完全替代了语言虚拟机的静态编译器,从此不再需要解释器。相比现有的编译机制有以下明...【详细内容】
2019-08-30  Tags: 编译器  点击:(200)  评论:(0)  加入收藏
▌简易百科推荐
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(1)  评论:(0)  加入收藏
程序是如何被执行的&emsp;&emsp;程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(9)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(19)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(23)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(24)  评论:(0)  加入收藏
一个项目的大部分API,测试用例在参数和参数值等信息会有很多相似的地方。我们可以复制API,复制用例来快速生成,然后做细微调整既可以满足我们的测试需求1.复制API:在菜单发布单...【详细内容】
2021-12-14  AutoMeter    Tags:AutoMeter   点击:(20)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条