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========================================================================================================

posted on 2019-07-25 00:41  疾速瓜牛  阅读(6185)  评论(0编辑  收藏  举报

导航