macOS 中神秘的 GCC

这个事情要从编译一个简单的代码开始说起。


gcc 和 g++ 的区别

gcc 是 GCC 中的 GUN C Compiler,C 编译器。

g++ 是 GCC 中的 GUN C++ Compiler,C++编译器。

就本质而言,gccg++ 并不是编译器,也不是编译器的集合,它们只是一种驱动器,根据参数中要编译的文件的类型,调用对应的 GUN 编译器而已。

gccg++ 都可以编译 C 和 C++ 文件,只是处理方式不同。 可以参考 GCC的gcc和g++区别 这篇文章。

交换变量-指针

下面这段代码很简单,利用 C 指针实现交换两个变量的目的。

源文件:mz_swap.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
// 使用指针的方式交换两个变量
int mz_swap(int *ap, int *bp);
int main(int argc, char * argv[]) {
int xp = 901;
int yp = 902;
printf("--Before-- xp = %i, yp = %i\n", xp, yp);
mz_swap(&xp, &yp);
printf("--After--- xp = %i, yp = %i\n", xp, yp);
return 0;
}
int mz_swap(int *ap, int *bp) {
if (NULL == ap || NULL == bp) {
return -1;
}
int tp = *ap;
*ap = *bp;
*bp = tp;
return 0;
}

在 macOS 下使用 gcc 编译 mz_swap.c,产生可执行文件 exec_main.out,如下操作:

1
gcc mz_swap.c -o exec_main.out

编译成功,执行 ./exec_main.out,正常输出结果:

1
2
--Before-- xp = 901, yp = 902
--After--- xp = 902, yp = 901

采用 g++ 来编译,如下操作:

1
g++ mz_swap.c -o exec_main.out

报出如下警告,该警告的意思是在 C++ 模式下强制编译 C 文件,这里可以不予理会。

1
clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated [-Wdeprecated]

虽然有警告,但是编译可以产生可执行文件 exec_main.out 且可以正常执行的,执行结果跟上面一样。

交换变量-引用

大家都知道,C 没有 引用 这个概念,而 C++ 是有 引用 概念的,可以在此了解 C++ 引用.

试一下使用 gcc 编译使用 引用 方式编写的 C 代码,按照说法,gcc 是无法编译通过的。

源文件:mz_swap_ref.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
// 使用引用的方式交换两个变量
int mz_swap_ref(int &ap, int &bp);
int main(int argc, char * argv[]) {
int xp = 901;
int yp = 902;
printf("--Before-- xp = %i, yp = %i\n", xp, yp);
mz_swap_ref(xp, yp);
printf("--After--- xp = %i, yp = %i\n", xp, yp);
return 0;
}
int mz_swap_ref(int &ap, int &bp) {
int tp = ap;
ap = bp;
bp = tp;
return 0;
}

编译

1
gcc mz_swap_ref.c -o exec_main.out

编译失败,错误信息如下:

1
2
3
4
5
mz_swap_ref.c:4:21: error: expected ')'
int mz_swap_ref(int &ap, int &bp);
^
mz_swap_ref.c:4:16: note: to match this '('
int mz_swap_ref(int &ap, int &bp);

既然按照 C 的编译方式不行,那就换成 g++ 来编译。

1
g++ mz_swap_ref.c -o exec_main.out

编译成功,执行可执行文件

1
./exec_main.out
1
2
--Before-- xp = 901, yp = 902
--After--- xp = 902, yp = 901

大家可以自己动手试试,使用 clang 来编译 mz_swap_ref.c 和使用 gcc 来编译都是报错,使用 clang++g++ 编译都是没有问题的。

在博客 GCC: Homebrew 安装 GCC 和 Binutils 中,我已经分享了在 macOS 中的 gccg++ 不是 GNU 提供的,而是 Apple 自己的 clang。

这里要说明的是即使使用自己安装的 gcc(gcc-4.9) 来编译 mz_swap_ref.c 也是无法编译通过的,验证如下:

1
gcc-4.9 mz_swap_ref.c -o exec_main.out

错误信息:

1
2
3
4
5
mz_swap_ref.c:4:21: error: expected ';', ',' or ')' before '&' token
int mz_swap_ref(int &ap, int &bp);
^
mz_swap_ref.c:20:21: error: expected ';', ',' or ')' before '&' token
int mz_swap_ref(int &ap, int &bp) { ^

gcc、g++ 和 clang 的关系

在 macOS 中,gcc 以某种方式指向 llvm-gcc 编译器,g++ 亦如此。

1
In Apple's version of GCC, both cc and gcc are actually symbolic links to the llvm-gcc compiler. Similarly, c++ and g++ are links to llvm-g++.

llvm-gcc 是 c/c++/oc 的编译器,用了 gcc 前端和命令行界面的 llvm.

1
llvm-gcc is a C, C++, Objective-C and Objective-C++ compiler. llvm-g++ is a compiler driver for C++. llvm-gcc uses gcc front-end and gcc's command line interface.

接下来,我们看一下 llvm-gcc,可以使用 which llvm-gcc 看一下 llvm-gcc 所在位置是 /usr/bin/llvm-gcc,打开目录可以看出其实是一个符号链接,如下图所示:
1

1
ls -l /usr/bin/llvm-gcc
1
/usr/bin/llvm-gcc -> clang

它们是统一指向 clang 的符号链接,可以看其原始指向,llvm-gcc 指向 clangllvm-g++ 指向 clang++

二者都在 /usr/bin/ 目录下:
1

其实在 macOS 中 cc 编译器也指向 clang

总结

通过上面的例子,我们至少可以学到如下几点知识:

1. C 语言规范中没有 引用 的概念,使用 C 编译器无法使其编译通过,但是使用 C++ 编译器是可以编译通过的。这是因为各自的编译器是遵循语言规范的。

2. macOS 中的 gccg++ 苹果开发者们并没有去改造和重写它们,只是分别指向 clangclang++ 编译器。

3. 如果不想使用 macOS 中的 gccg++,就需要自己重新安装 GNU 的,安装和使用方法已经在博客中有说明。


扫码关注,你我就各多一个朋友~

坚持原创技术分享!