1.c与c++编译方式

(1)gcc和g++都可以编译.c文件,也都可以编译.cpp文件。g++和gcc是通过后缀名来辨别是c程序还是c++程序的(这一点与Linux辨别文件的方式不同,Linux是通过文件信息头辨别文件的)。
(2)在gcc看来,.c文件会以c方式去编译,.cpp文件则是以c++的方式去编译,注意,gcc不会主动去链接c++用到库stdc++,所以用gcc编译cpp文件时需要手动指定链接选项-lstdc++。而对于g++,不管是.c还是.cpp文件,都是以c++方式去编译。
(3)还需要注意,并不是说__cpluscplus 是g++编译器才会定义的宏,确切的说,是只有以c++编译的方式去编译文件的宏才会定义的宏,这样说来,gcc编译.cpp文件、g++编译.c、.cpp文件,这个 __cplusplus都会被编译器定义。

2.c++调用c程序

假设c程序是之前写好的具有价值的静态库,该库是由add.o和sub.o编译而成,而add.c和sub.c是由c语言写的:
add.h和add.c

  1. //add.h
  2. #ifndef __ADD_H__
  3. #define __ADD_H__
  4. int add(int, int);
  5. #endif /* __ADD_H__ */
  6.  
  7. //add.c
  8. #include "add.h"
  9.  
  10. int add(int a, int b)
  11. {
  12. return a + b;
  13. }
  14.  
  15. sub.hsub.c
  16.  
  17. //sub.h
  18. #ifndef __SUB_H__
  19. #define __SUB_H__
  20. int sub(int, int);
  21. #endif /* __SUB_H__ */
  22.  
  23. //sub.c
  24. #include <stdio.h>
  25. #include "sub.h"
  26. int sub(int a, int b)
  27. {
  28. printf("%d - %d = %d\n", a, b, a - b);
  29. return ;
  30. }

将各自编译成.o文件,并打包成静态库

  1. $ gcc -c add.c
  2. $ gcc -c sub.c
  3. $ ar cqs libadd.a *.o

这样就生成了libadd.a静态库。
若要编译成动态库,则

  1. $ gcc -shared -fPIC *.o -o libadd.so

main.cpp是c++写的,用g++编译。

  1. //main.cpp
  2. #include "add.h"
  3. #include "sub.h"
  4. #include <stdio.h>
  5.  
  6. int main(void)
  7. {
  8. int c = add(, );
  9. sub(, );
  10. printf("c = %d\n", c);
  11. return ;
  12. }

编译:

报错未定义的add和sub函数。
通过nm确实是可以看到add和sub标号的存在的:

原因在于,main.cpp是c++文件,用g++编译器编译时(gcc也是一样的结果),会优先选择cpp的编译方式,也就是会用cpp的编译方式去编译add()、sub()函数。然而,它们是.c文件,用的是c语言的方式去编译的,所以出现如上问题。注意,cpp编译器是兼容c语言的编译方式的,所以在编译cpp文件的时候,调用到.c文件的函数的地方时,需要用extern “C”指定用c语言的方式去编译它:

//使得add.h、sub.h里面的代码用c语言的方式去编译

  1. extern "C"{
  2. #include "add.h"
  3. #include "sub.h"
  4. }
  5. #include <stdio.h>
  6. int main(void)
  7. {
  8. int c = add(, );
  9. printf("c = %d\n", c);
  10. return ;
  11. }

编译运行 


特别注意extern “C”是c++方式编译才认识的关键字。

3. c语言调用c++程序

c语言用c方式编译,c++程序用c++方式,要使得c语言能调用c++程序无非有2种方法(c++调用c也是一样)
(1) c语言程序用c++方式编译
既然是c语言调用c++程序,肯定是要采取c++方式编译,所以觉得这个没什么意义。操作很简单,用g++方式编译c程序即可,注意,g++会对语法、类型等更为严格的检查。
(2) c++程序用c语言方式编译
a. c方式编译和c++方式编译,其差异就在于符号表标识符。同一个函数名,在c方式编译的其函数名跟编译前的函数一致,c++方式编译的则是以函数名结合参数作为编译后的函数名。要确保文件以.c方式编译,可以利用__cplusplus,这个宏在c++编译的方式才会定义的宏,结合之前的extern C用法如下:

  1. #ifdef __cplusplus
  2. extern "C"{
  3. #endif /* __cplusplus */
  4.  
  5. //用c方式编译的代码
  6.  
  7. #ifdef __cplusplus
  8. }
  9. #endif /* __cplusplus */

这样,对于一份.c文件,采用gcc编译时候没有定义__cplusplus,宏判断不起作用,且自是用c语言的方式编译,采用g++编译定义了_cplusplus,经过上面宏判断,所以还是会以c语言的方式编译。注意,extern “C”是g++才具有的关键字,gcc并没有,所以如果用gcc编译而不加以宏判断直接使用extern “C”那么就会出现语法错误。
用c方式去编译c++文件,还要注意重载函数。c方式编译的c++文件决定不能出现重载函数。尝试extern “C”中出现重载函数:

  1. #ifdef __cplusplus
  2. extern "C"{
  3. #endif /* __cplusplus */
  4. int func(int a, int b){
  5. return a + b;
  6. }
  7.  
  8. void func(const char* str){
  9. printf("str = %s\n", str);
  10. }
  11. #ifdef __cplusplus
  12. }
  13. #endif /* __cplusplus */
  14.  
  15. int main(void)
  16. {
  17. func("hello");
  18. return ;
  19. }

提示找不到func函数。在c语言中肯定不能出现同名函数,不然编译器怎么知道它要调用的是哪一个。去除extern “C”关键字,也就是使其采用c++方式编译,编译结果:

编译通过。通过nm命令可以查看可执行程序的符号表:

可见c++编译方式会将重载函数名结合参数形成唯一的函数名。但是c方式编译的可执行文件却并非如此,函数名经过编译后的符号还是之前的函数名,所以出现找不到调用函数的现象。
b. c程序要调用c++写的程序,涉及到的可能有:c程序调用c++的普通函数,c程序调用c++的重载函数,c程序调用c++的成员函数(包括虚函数)。c程序自然是采用c的方式去编译的,即是采用gcc编译器,然而c++是采用c++方式编译,所以要强制将c++代码以c的方式去编译。
(1) c程序调用c++的普通函数

  1. add.hadd.cpp
  2. //add.h
  3. #ifndef _ADD_H_
  4. #define _ADD_H_
  5. int add(int, int);
  6. #endif /* _ADD_H_ */
  7. //add.cpp
  8. #include <iostream>
  9. extern "C" { //以c的方式去编译
  10. int add(int a, int b)
  11. {
  12. std::cout<<"a + b = "<< a + b << std::endl;
  13. return ;
  14. }
  15. }

注意不能在声明add函数的add.h中指定以c的方式去编译,因为add.h是要被main.c文件包含的,c方式编译时并不能认得extern “C”关键字。

  1. main.c
  2.  
  3. //main.c
  4. #include <stdio.h>
  5. #include "add.h"
  6. int main(void)
  7. {
  8. add(, );
  9. return ;
  10. }

编译运行:

因为add.cpp用到标准c++库,所以要手动链接该库。
(2)c调用c++的重载函数
前面已经知道,在extern “C”中是不允许出现重载函数的,因为c方式下的程序并不支持重载函数,所以需要对重载函数进行接口封装。使用extern “C”是要告诉编译器按照c的方式来编译封装接口,接口里面的函数还是按照c++的语法和c++方式编译。

  1. add.hadd.cpp
  2.  
  3. //add.h
  4. #ifndef _ADD_H_
  5. #define _ADD_H_
  6. int add_ii(int, int);
  7. int add_c(char);
  8. #endif /* _ADD_H_ */

上面两个函数是供c程序调用的,自然需要用c方式编译。而这两个函数的实现体可以去调用c++的重载函数,这就完美契合了。

  1. //add.cpp
  2. #include <iostream>
  3. int add(int a, int b)
  4. {
  5. std::cout<< "a + b = " << a + b << std::endl;
  6. return ;
  7. }
  8.  
  9. int add(char c)
  10. {
  11. std::cout << "c = " << c << std::endl;
  12. }
  13.  
  14. extern "C" int add_ii(int a, int b) //供c程序调用,用c方式编译
  15. {
  16. add(a, b);
  17. }
  18.  
  19. extern "C" int add_c(char c)
  20. {
  21. add(c);
  22. }

main.cpp

  1. //main.cpp
  2. #include <stdio.h>
  3. #include "add.h"
  4.  
  5. int main(void)
  6. {
  7. add_ii(, );
  8. add_c('d');
  9. return ;
  10. }

编译运行:

---------------------
作者:echo_bright_
来源:CSDN
原文:https://blog.csdn.net/qq_29344757/article/details/73332501
版权声明:本文为博主原创文章,转载请附上博文链接!

c语言和c++的相互调用的更多相关文章

  1. Visual Studio 2019 使用C语言创建动态链接库(Dll)并使用C语言和C#实现调用

    参考网址:https://blog.csdn.net/weixin_34976988/article/details/99625533 一.创建DLL1.建立动态链接库项目 2.创建头文件和源文件 删 ...

  2. C语言和C++篇

    C语言和C++篇 基本上所有主流的编程语言都有String的标准库,因为字符串操作是我们每个程序员几乎每天都要遇到的.想想我们至今的代码,到底生成和使用了多少String!标题上所罗列的语言,可以看成 ...

  3. 从C,C++,JAVA和C#看String库的发展(一)----C语言和C++篇

    转自: http://www.cnblogs.com/wenjiang/p/3266305.html 基本上所有主流的编程语言都有String的标准库,因为字符串操作是我们每个程序员几乎每天都要遇到的 ...

  4. R语言- 实验报告 - 利用R语言脚本与Java相互调用

    一. 实训内容 利用R语言对Java项目程序进行调用,本实验包括利用R语言对java的.java文件进行编译和执行输出. 在Java中调用R语言程序.本实验通过eclipse编写Java程序的方式,调 ...

  5. java 与 R 相互调用

    https://www.r-project.org/ http://cos.name/2013/08/r-rjava-java/ http://blog.csdn.net/hwssg/article/ ...

  6. C语言和C++中动态申请内存

      在C语言和C++的动态内存的使用方法是不同的,在C语言中要使用动态内存要包含一个头文件即 #include<malloc.h> 或者是#include<stdlib.h>  ...

  7. CHENGDU1-Python编程语言和PEP8规范

    CHENGDU1-Python编程语言和PEP8规范 PEP8规范6条? 答:PEP8规范说白了就是一种规范,可以遵守,也可以不遵守,遵守PEP8可以让代码的可读性更高. 代码编排:---缩进,4个空 ...

  8. Lua 与 OC 相互调用

    本文主要讲如何完成lua和object-c的相互调用.       lua是一种脚本语言,可以方便的移植到各种宿主语言中,并且可以支持热更新,在游戏开发中也能当做主要的语言来编写游戏的逻辑,但是要接入 ...

  9. C语言与汇编语言相互调用原理以及实例

    C语言与汇编语言相互调用原理以及实例 1.原理 其实不管是C语言还是汇编语言想要执行都是最终编译链接成为二进制文件. 这里一定要明确编译和链接是两个步骤,生成的文件格式也是不一样的. 编译生成的文件是 ...

随机推荐

  1. SQL2005 异常处理机制(Begin try Begin Catch)

    将可能会出错的sql 写在begin try...end try 之间,若出错,刚程序就跳到紧接着的begin try...end try 的beign catch...end catch中,执行be ...

  2. CAD2015 安装出错

    Autodesk安装失败后回滚连带把在D盘创建的安装目录都给删除掉了. 把.net 4.6卸载干净之后就可以成功安装CAD2015了.只安装.net 4.5就行了.

  3. 【bzoj1444】[Jsoi2009]有趣的游戏

    1444: [Jsoi2009]有趣的游戏 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1007  Solved: 334[Submit][Statu ...

  4. 【NOI2002】荒岛野人

    [题解] 可以枚举m 那么任意两个野人之间有 c[i]+x*p[i]=c[j]+x*p[j] (mod m)  无解,或 x 的最小值<=min(l[i] , l[j]) 化为丢番图方程:(p[ ...

  5. c++原型模式(Prototype)

    原型模式是通过已经存在的对象的接口快速方便的创建新的对象. #include <iostream> #include <string> using namespace std; ...

  6. jsoup 的简单应用

    导入相关jar包 package jsoup.zr.com.utils; import java.io.IOException; import java.util.List; import org.j ...

  7. opennebula 创建镜像数据块

    {","csrftoken":"f5454a02dea7b4a7d5d50b482a762b57"}

  8. jquery中children()

  9. js实现在表格中删除和添加一行

    <!DOCTYPE html><html> <head> <title> new document </title> <meta ht ...

  10. markdown的图片外链

    markdown的图片用本地的很不方便,今天试用了一下七牛的服务,感觉很好用.推荐一下,免费的服务够用并且比较友好.