• gcc和g++


      1. cc/gcc/g++/CC

      gcc和g++都是GUN(组织)的编译器。gcc主要用于C语言程序编译,而C++语言通常使用g++进行编译。linux下cc一般是一个符号连接,指向gcc;而CC则一般是makefile里面的一个变量。通常makefile中会定义 "CC = gcc"

    cc是Unix系统的C Compiler,而gcc则是GNU Compiler Collection,GNU编译器套装。gcc原名为Gun C语言编译器,因为它原本只能处理C语言,但gcc很快地扩展,包含很多编译器(C、C++、Objective-C、Ada、Fortran、 Java)。因此,它们是不一样的,一个是古老的C编译器,一个是GNU编译器集合,gcc里面的C编译器比cc强大多了,因此没必要用cc。

    Linux下的cc是gcc符号连接,可以通过$ls –l /usr/bin/cc来简单察看,该变量是make程序的内建变量,默认指向gcc。cc符号链接和变量存在的意义在于源码的移植性,可以方便的用 gcc来编译老的用cc编译的Unix软件,甚至连makefile都不用改,而且也便于Linux程序在Unix下编译。

     

    2. gcc和g++的关系

    2.1 gcc 和 g++都可以用来编译c或者c++程序。

      后缀为.c的,gcc把它当作是C程序,而g++当作是c++程序;后缀为.cpp的,两者都会认为是C++程序,注意,虽然C++是C的超集,但是两者对语法的要求是有区别的。C++的语法规则更加严谨一些。

    2.2 编译阶段,g++会调用gcc,对于C++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接统统用g++了,这就给人一种错觉,好像cpp程序只能用g++似的。但可以使用gcc选项 -lstdc++ 来与c++的库链接,所以可以认为,对于c++程序来说, g++ 等价于 gcc -lstdc++。

     

    2.3 宏__cplusplus定义与否与使用gcc还是g++编译无关,而与源程序被认为是c或c++语言有关

    如下的程序保存为 main.c

    View Code
     1 #include<stdio.h>
     2 
     3 int main(int argc, char* argv[])
     4 {
     5 #ifndef __cplusplus
     6     printf("C...\n");
     7 #else
     8     printf("C plus plus...\n");
     9 #endif
    10     printf("in main func...\n");
    11     return 0;
    12 }
    13 
    14 int func(int i, char c)
    15 {
    16     return 0;
    17 }

    使用gcc进行编译,会将.c文件当作C程序,得到输出:

    使用g++进行编译,会将.c文件当作C++程序,得到输出:

     

     

    将上述main.c保存为main.cpp,则gcc和g++都将其当作C++程序源文件,分别编译运行,得到:

    总结:__cplusplus标示符用来判断程序是用c还是c++编译程序编译的。当编译c++程序时,这个标示符会被定义,编译c程序时,不会定义。

     

    3. extern C

    经常在.cpp代码中看到类似如下的代码:

    #ifdef __cplusplus
        extern "C"
        {
     #endif
        //此处若干行代码
    
    #ifdef __cplusplus
        }
    #endif

      这段代码的意思是,如果这是一段cpp的代码,那么要使用C编译器编译加入extern "C"{和}之间的代码。目的是为C++程序中调用C编写的库函数提供便利。这主要是由于C++和C在编译过程中生成函数符号时不同机制造成的。

      C++支持函数重载而C不支持,同名的函数经过C++和C编译后在符号表中的名字不同。例如在上述代码main.c中的函数func(int i, char c),经过gcc -S 编译和g++ -S 编译后所得到的汇编代码中的函数名不一样。C++编译器会根据函数的参数类型和顺序生成不一样的函数名从而实现函数重载,(不同的编译器可能生成的名字不同,但是都采用了相同的机制)。符号表中的名字的名字包含了函数名、函数参数数量及类型信息,C++ 就是靠这种机制来实现函数重载。

      gcc -S main.c得到的main.s 中的func函数名:

      

      g++ -S main.c 得到的main.s中的func函数名:

      

      g++编译时的确会将参数的类型和顺序作为函数名的一部分来生成。

      如下是一个简单的关于C和C++函数名以及extern "C"相关的测试程序

    c.h
    1 #include <stdio.h>
    2 void cprint();
    c.c
    1 #include "c.h"
    2 
    3 void cprint()
    4 {
    5     printf("c print...\n");
    6 }
    cplus.cpp
     1 #include <iostream>
     2 //extern "C"
     3 //{
     4 #include "c.h"
     5 //}
     6 using namespace std;
     7 
     8 int main(int argc, char* argv[])
     9 {
    10     cprint();
    11     return 0;
    12 }

      执行编译命令 gcc -c c.c 得到c.o,这时根据C语言编译规则进行。执行gcc -c cplus.cpp,这时根据C++语言编译规则进行。之后执行g++ *.o -o a.out,得到:

      

      可见是用c.o中的函数时,找不到函数名。而将cplus.cpp中的代码修改如下

    cplus.cpp
     1 #include <iostream>
     2 extern "C"
     3 {
     4 #include "c.h"
     5 }
     6 using namespace std;
     7 
     8 int main(int argc, char* argv[])
     9 {
    10     cprint();
    11     return 0;
    12 }

      在分别执行gcc -c c.c 和gcc -c cplus.cpp得到.o 文件后,执行g++ *.o -o a.out,发现链接通过。

      当未使用extern "C"时,gcc将c.h中的函数cprint作为C++函数进行编译,得到名如__Z6cprintv之类的函数名,而gcc单独编译c.c时,则是按照C规则,生成函数名为cprint,所以在链接时,在c.o中找不到形如__Z6cprintv函数。

  • 相关阅读:
    do...while(0)的妙用
    用位运算实现求绝对值-有效避开ifelse判断
    经典排序算法的C++ template封装
    DOM学习总结(二) 熊削铁如泥
    标签设计Loop标签
    asp:树型select菜单
    自家用的DataReapter分页代码
    正则表达式(一)
    C#中利用正则表达式实现字符串搜索
    解读C#中的正则表达式
  • 原文地址:https://www.cnblogs.com/litterrondo/p/2882023.html
Copyright © 2020-2023  润新知