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