• C/C++混合编程编译问题


    以下为本实验使用的编译器版本:

    系统环境为:

    目的:搞清以下几个问题

    1.  g++能否编译c文件

    2.  g++编出的s文件和gcc编出来的有何异同

    3.  __cplusplus宏在何时被定义

    4. c调用c++的注意事项

    5. c++调用c的注意事项

    6. 针对上述问题的makefile怎么写比较好


    问题1:g++能否编译c文件

    g++  -E  hello.c -o   g++_hello.i

    gcc   -E  hello.c -o   gcc_hello.i

    用beyond compare去对比一下二者的异同,可以看到

    g++_hello.i关键是多了这个extern "C" 然后把stdio.h中的函数声明和类型定义全部包住

    “这个标识符的作用把标识符作用域的数据类型采用gcc去编译”

    【我感觉有点不对,这可能是stdio.h中自带的#ifdef __cpluplus导致的,而不是预处理器搞的,我来看一下】

     上面是预处理的部分,下面看一下编译结果的异同

    gcc -S hello.c -o gcc_hello.s

    g++ -S hello.c -o g++_hello.s

    对比结果如下图所示,显然对于c文件的编译,g++还是对函数名动了手脚

    这里有个疑惑,为什么是.arch armv6,我的树莓派明明是armv7的架构啊?

     

    可以看到,生成的汇编指令中的函数名是不同的,生成的汇编指令也不同,这里和这篇文章说的就开始有出入了

    后缀为.c的文件,gcc当做c程序去编,g++当做c++程序去编

    https://blog.csdn.net/qq_21792169/article/details/85097822

    gcc  -c hello.c -o gcc_hello.o

    g++ -c hello.c -o g++_hello.o

    g++编出来的明显比较大

    下面看一下生成的.o文件的反汇编结果是否相同呢

    objdump -S gcc_hello.o > gcc_hello.obj

    objdump -S g++_hello.o > g++_hello.obj

    【可以看到也是有点差别的,不过反汇编后的文件大小竟然是一样的,而.o文件大小不一样,这tm是什么原因?】

     好的,继续往下看,我们现在把hello.o 和main.o进行链接,其中main.c中调用了hello中的函数,看看gcc和g++去编译链接会发生什么事情

    gcc main.c hello.c -o gcc_main

    g++ main.c hello.c -o g++_main

    都没有任何问题,编译链接,包括运行都正常

    分步看一下

    gcc -S main.c -o gcc_main.s

    g++ -S main.c -o g++_main.s

    好的,没有任何问题

    现在的结论是,g++可以编译c文件,编出来的结果是按照c++的方式去给函数命名,并且在main.c 中和hello.c中遵循同样的symbol命名,所以编译链接都是没有问题的

    下面开始看问题3,4,5

    我现在main.c中想要调C++编写的一个类的接口

    Makefile为:

     先用gcc不链接stdc++(链接上stdc++也不行,不认识就是不认识)去make

    【结果gcc压根就不认识class等C++的关键字,说好的gcc能编呢?那用g++去编呢,没有任何问题,完全通过】

    结论:gcc编译c文件,c文件中一定不能出现c++的语法和库操作,如果有就用g++去编(g++把c文件当做c++去编)

    现在再做一个实验,把main.c改为main.cpp,然后用g++去编,嗯,测试下来没问题,改用gcc去编

    case 1: 不链接stdc++

    case 2: 链接stdc++

    嘿嘿,这里就有趣了,终于出现了我们想看到的状况,main.cpp中链接不到hello函数的symbol,嘻嘻,hello.c是用gcc编译的,你去看一下他的s文件,前面有

    然后着重看一下main.cpp的s文件

    gcc -lstdc++ -S main.cpp -o gcc_maincpp.s

    我截取了一小部分

    看到没,这怎么可能链接到hello.o中的hello符号呢?

    所以这里又可以得到几个结论:

    1、后缀.cpp的,gcc与g++都当成c++程序(gcc也可以编C++程序),gcc编的时候注意链接stdc++

    2、C想调用C++,要么用g++去编译C,要么把.C后缀改为.CPP

    3、gcc不能自动链接c++库,g++会自动链接c++库

    g++ 会自动进行 C++ 标准库的连接;用 gcc 连接 C++ 程序也可以,但是需要人为指定连接 C++ 标准库,否则就会出现undefined reference to `__gxx_personality_v/0' 之类的错误,gcc编译 c++ 程序需要添加 -lstdc++   sample: gcc -lstdc++ -o test test.cpp

     OK,到这一步,我们应该知道了extern "C"的用处了,我们直接去hello.h中

    然后重新make,咦?

     这里注意:如果使用gcc编译,不使用__cplusplus宏,直接用extern “C”则会报错。报错为expected identifier or ‘(’ before string constant

    而直接在main.cpp中像上图那样把#include "hello.h"括起来则是可以的

     原因是这样的,extern "C" 放在hello.h头文件中时,gcc以C程序的方式在处理hello.c时发现自己不认识extern "C",所以出了这个问题

    C 语言中不支持extern "C" 声明,在.c 文件中包含了extern "C" 时会出现编译语法错误(error: expected identifier or ‘(’ before string constant )

    而在main.cpp中,C++是认识这个的,所以可以直接用extern "C" {}包起来。

     在hello.h中改成我们最熟悉的这种形式,哈哈哈,make后就一切OK了

     

    因此,我们又可以得到一些结论:

    1、  在函数声明前加上extern “C”,这是在提醒g++编译链接这个函数时按照c函数机制去链接。同样如果在程序中函数实体前加extern “C”,这是在提醒编译器整个函数用gcc按照C规则去编译。切记理解编译与链接是两码事情

    2、 在main.cpp中的#include "hello.h"上下加上extern "C" {}也是可以的,只不过这样看着不美观,因此,一般都在.h文件中通过#ifdef __cplusplus来加

    关于__cpluplus宏何时会被定义的问题:

    记住一个简单的原则:只有当用gcc编译.c程序时才会按照c规则编译程序,其它情况下这个宏都是被定义的

    这里引用一张图,很容易说明这一点:

     

    gcc test.c && ./a.out        @结果是:a+b=1314

    g++ test.c && ./a.out        @结果是:a+b=520

    gcc test.cpp && ./a.out      @结果是:a+b=520

    g++ test.cpp && ./a.out      @结果是:a+b=520
    ---------------------
    作者:HeroKern
    来源:CSDN
    原文:https://blog.csdn.net/qq_21792169/article/details/85097822
    版权声明:本文为博主原创文章,转载请附上博文链接!

    ===================================================================

    好了,上面同时回答了C调用C++的情况,以及CPP调用C的情况。

    现在我们来简单总结一下重要的结论:

    1. C调C++的类或接口时,如果想用gcc编.C文件的话,C程序中一定不能出现C++的语法关键字和库,否则根本过不了编译(C不兼容C++),如果用g++编译的话则OK,g++会把这个.C文件当做c++去编(因为C++是兼容C的,所以这样是可行的)

    2. C++调用C的接口时,.C可以用gcc去编,.C++既可以用g++去编,也可以用gcc去编(需要显式链接-lstdc++)

    3. 一般来说,.C的部分的头文件.h中用#ifdef __cpluplus extern "C" {},可以使得C/C++混合编译时很方便,main.cpp会在链接时,按照C的机制去链接

     

     最后说一说,C/C++混合编程时的makefile怎么写比较好

    以下内容引用自:https://blog.csdn.net/qq_21792169/article/details/85097822

    1、如果是工程师自己写编译规则,那么在Makefile中应该检测.c和.cpp文件分别用gcc与g++编译,最后统一链接。

    2、如果是IDE环境,由IDE环境决定,在qt中qmake是将.c文件用gcc编译,g++编译。

    3、那么g++如何调用c程序封装的函数或者库呢,从上述讲解知识可以看到c++程序直接调用c程序接口,这个肯定报错提示找不到库函数,c和c++处理函数接口机制不一样,所以在c++程序中调用c程序应该在函数声明前加上extern “C”,这是在提醒g++编译链接这个函数时按照c函数机制去链接。同样如果在程序中函数实体前加extern “C”,这是在提醒编译器整个函数用gcc按照C规则去编译。切记理解编译与链接是两码事情。

    【有读者可能就问了,既然g++能够编译c程序,gcc还有必须要存在吗?这个答案是肯定的。

    原因一:操作系统全是按照c规则编写与编译(除开head.S等文件中的汇编),很多系统库文件都是按照c规则写与编译的,

    如果采用g++去编译这一部分,那么生成的可执行文件会非常大,以及程序运行效率大大降低;还可能存在不能正常编译通过情况,就是上述对比实验中函数链接接口不一样。

    编译器优化性能也是有限的,系统内核是整个上层应用的心脏,必须做到最高效率,不许存在过多冗余代码(编译器的问题)

    原因二:做单片机出身的工程师对c语言扣字节操作应该是一种信仰,做上层应用的工程师更偏向c++面向对象,

    一个大项目基本都是通力合作完成。所以gcc用来编译c程序(效率问题,c实现对系统接口二次封装),g++编译c++程序。】 

     Done========================================================================================================

  • 相关阅读:
    CF995A Tesla
    CF961D Pair Of Lines
    P1186 玛丽卡
    CF986B Petr and Permutations
    hdu6331 Problem M. Walking Plan
    Edison UVALive3488
    Be a Smart Raftsman SGU475
    100198H Royal Federation
    100197G Robbers
    Evil Book -- CodeChef
  • 原文地址:https://www.cnblogs.com/Arnold-Zhang/p/11241757.html
Copyright © 2020-2023  润新知