C/C++混合编译

一、C++函数重载

1、编写代码

(1)sum.h

#ifndef __SUM_H__
#define __SUM_H__

int sum(int a, int b);
float sum(float a,float b);

#endif

(2)sum.cpp

#include "sum.h"

int sum(int a, int b) {
    return a + b;
}

float sum(float a, float b) {
    return a + b;
}

(3)main.cpp

#include <iostream>
#include "sum.h"
using namespace std;

int main()
{
    cout  << "1+4 = " << sum(1,4) << endl;
    cout  << "1.1+4.4 = " << sum(1.1f,4.4f) << endl;
    return 0;
}

2.编译运行

在这里插入图片描述

3.使用nm命令查看可执行文件中的符号表
// nm 可执行文件名 或 目标文件名
nm a.out

在这里插入图片描述

4.C++中的函数重载实质

这两个函数名看是经过了C++的名称修饰(name mangling),因为在C++中,函数重载会导致相同的函数名被修改以包含参数类型信息,以用来区分同名函数。

(1) _Z3sumff:这是一个函数名,sum(float a, float b)经过了C++的名称修饰,表示一个接受两个float参数的函数。

(2) _Z3sumii:同样,sum(int a, int b)这个函数名也经过了C++的名称修饰,表示一个接受两个int参数的函数。ii表示两个参数都是int类型。

结论:c++里面的函数重载不是真的说可以同名,g++在编译的时候将这两个sum的名字给换掉了

5.函数重载的使用时机

1.必须在同一个作用域中才有效

2.函数的名字必须相同

3.参数不一样

​ (1)参数个数不用

​ (2)参数类型不一样

​ (3)参数类型一样,但是参数顺序不一样

4.函数重载跟返回值没有关系

二、C/C++混合 编程

1、为什么要这样做:

混合编程(Mixing C and C++ Programming)是指在同一个程序中同时使用 C 和 C++ 语言的特性,这在实际项目中是非常常见的。

  1. 模块化编程:将 sum.c 编译成动态链接库后,可以将函数 sum 独立地组织成一个模块,这样可以方便地在其他程序中复用。
  2. 隐藏实现细节:动态链接库中只包含了函数的二进制实现,而隐藏了具体的源代码。这样可以保护代码的知识产权,并且在使用库的程序中无需了解具体实现细节。
  3. 提高可维护性:动态链接库可以独立编译和更新,这样在更新库时只需替换动态链接库文件,而不需要重新编译使用库的程序。
  4. 简化编译和链接过程:在编译 main.cpp 文件时,通过 -L-l 选项指定了库文件的路径和名称,编译器会自动查找并链接指定的动态链接库。这样可以简化编译和链接的过程,减少了手动操作的复杂性。
  5. 跨语言兼容性:使用动态链接库的方式可以实现 C 和 C++ 之间的互操作,因为动态链接库中的函数可以以 C 的方式进行链接,并且可以在 C++ 中调用。

**总的来说:**使用混合编程和动态链接库的方式能够充分发挥 C 和 C++ 语言的优势,提高项目的效率、可维护性和可扩展性,是现代软件开发中常见的实践之一。

2、好处和作用:
  1. 模块化编程:将 sum.c 编译成动态链接库后,可以将函数 sum 独立地组织成一个模块,这样可以方便地在其他程序中复用。

  2. 隐藏实现细节:动态链接库中只包含了函数的二进制实现,而隐藏了具体的源代码。这样可以保护代码的知识产权,并且在使用库的程序中无需了解具体实现细节。

  3. 提高可维护性:动态链接库可以独立编译和更新,这样在更新库时只需替换动态链接库文件,而不需要重新编译使用库的程序。

  4. 简化编译和链接过程:在编译 main.cpp 文件时,通过 -L-l 选项指定了库文件的路径和名称,编译器会自动查找并链接指定的动态链接库。这样可以简化编译和链接的过程,减少了手动操作的复杂性。

  5. 跨语言兼容性:使用动态链接库的方式可以实现 C 和 C++ 之间的互操作,因为动态链接库中的函数可以以 C 的方式进行链接,并且可以在 C++ 中调用。

    **总的来说:**使用动态链接库的方式能够提高代码的可维护性、复用性和安全性,同时简化编译和链接的过程,使得混合编程更加方便和灵活。

1.C调用C++的函数
1、编写代码

(1)sum.h

#ifndef SUM_H
#define SUM_H

int sum(int a, int b);

#endif

(2)sum.cpp

#include "sum.h"

int sum(int a, int b) {
    return a + b;
}

(3)main.c

#include <stdio.h>
#include "sum.h"

int main() {
    int result = sum(10, 20);
    printf("Sum: %d\n",result);
    return 0;
}
2、编译

(1)先将CPP的库编译成.so文件

g++ -shared -fpic -o libsum.so sum.cpp
gcc main.c -L./ -lsum

    -shared: 生成一个共享对象文件(也称为共享库或动态链接库)。共享对象文件可以被多个程序共享加载,它们在内存中只会存在一份拷贝,因此节省了系统资源。  

    -fpic: 生成位置无关的代码, 可以在内存中的任何位置运行 。在生成共享对象文件时,通常需要使用 -fpic 选项,以确保在程序运行时能够正确加载共享库并执行其中的代码。 

    -o: 指定输出文件的名称。 

    -L./:链接器选项,用于指定要搜索库文件的路径。. 表示当前目录,所以 -L./ 意味着在当前目录中搜索库文件。
    -lsum:链接器选项,用于指定要链接的库文件的名称。在这里,-lsum 意味着链接名为 libsum.so 的动态链接库。
    
libsum.so: 生成的共享对象文件的名称,lib为固定前缀!

在这里插入图片描述

无法执行,并且显示“对sum未定义的引用”。这是因为,C++在编译过程中修改了函数名。使用nm命令查看函数名:

在这里插入图片描述

函数名被更改!

3、链接

生成了.so文件后,它已经包含了编译后的目标代码,而不再依赖于原始的源代码文件。意思是 .so 文件中包含了编译后的目标代码,其中包括了函数的实现。

所以,使用gcc去编译main.c文件,当系统去查看sum的函数声明时,发现找不到sum函数,因为sum函数在编译阶段就已经被c++改名为了_Z3sumii!

解决方案1:

未定义的引用是因为sum函数被改名为了_Z3sumii,而sum.h文件,和main.c文件中,又找不到sum函数的定义。

此时,将main.c文件和sum.h文件中的sum函数调用和声明改为_Z3sumii就行了。

main.c

#include <stdio.h>
#include "sum.h"

int main() {
    int result = _Z3sumii(10, 20);
    printf("Sum: %d\n",result);
    return 0;
}


sum.h

#ifndef SUM_H
#define SUM_H

int _Z3sumii(int a, int b);

#endif

在这里插入图片描述

编译通过,运行成功!

解决方案2:使用 __cplusplusextern将C环境中的C++函数声明为:C++语言中的函数。

2.C++调用C的函数
1、编写代码
1.sum.h
#ifndef SUM_H
#define SUM_H
int sum(int a, int b);
#endif


2.sum.c
#include "sum.h"
int sum(int a, int b) {
    return a + b;
}


3.main.cpp
#include <iostream>
#include "sum.h"

int main() {
    int result = sum(10, 20);
    std::cout << "Sum: " << result << std::endl;
    return 0;
}
2、编译运行
gcc -shared -fpic -o libsum.so sum.c

g++ main.cpp -std=c++11 -L./ -lsum

结果:

在这里插入图片描述

3、解决方案:

通过 um可以查看到使用 gcc 编译过的 sum.c 是没有被改变名字的。

在这里插入图片描述

虽然库中sum函数名没有被改变,但是,使用 g++ 编译 main.cpp 的时候,编译器又将 sum 函数的名字改变了,导致系统又查找不到 sum 函数的声明和定义。

解决:使用 __cplusplusextern将C++环境中的C函数声明为:C语言中的函数。
#ifndef SUM_H
#define SUM_H

#ifdef __cplusplus
extern "C" {
#endif

int sum(int a, int b);

#ifdef __cplusplus
}
#endif

#endif

在 C++ 中,当编译器编译代码时,会自动定义一个名为 __cplusplus 的宏,并赋予一个非零值。而在编译 C 代码时,__cplusplus 宏是未定义的,即:编译 C 代码时,不会定义 __cplusplus 宏。

因此,通过使用 #ifdef __cplusplus,可以在 C++ 环境下执行特定的代码,而在 C 环境下则可以执行不同的代码。这种条件编译的方式使得我们可以编写可以同时被 C 和 C++ 编译器正确处理的代码。

在 C++ 中,可以使用 extern "C" 来声明 C 风格的函数,以便能够在 C++ 中正确地链接和调用这些函数。而在 C 中,不需要这样的声明,因此可以使用 #ifdef __cplusplus 来包含这些额外的声明,以确保它们只在编译 C++ 代码时被编译。

执行

在这里插入图片描述

执行通过!

  • 42
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值