广告位联系
返回顶部
分享到

C与C++接口函数相互调用的实现

C#教程 来源:互联网搜集 作者:秩名 发布时间:2020-03-22 19:47:47 人浏览
摘要

一、C 或 C++ 编译的四个步骤 (一) 预处理 在该步骤中,编译器将源程序中以#开头的语句进行处理。其中,#include 的原理是将目标文件内容导入本文件。 (二) 编译 在该步骤中,编译器将第一步生成的各个文件分别转换成汇编语言文件。在该过程中,所有函数的名

一、C 或 C++ 编译的四个步骤

(一) 预处理

在该步骤中,编译器将源程序中以“#”开头的语句进行处理。其中,#include 的原理是将目标文件内容导入本文件。

(二) 编译

在该步骤中,编译器将第一步生成的各个文件分别转换成汇编语言文件。在该过程中,所有函数的名称都会被转换成一个符号作为汇编文件中的唯一标识,对 C 语言函数一般直接用函数名称作为其唯一标识的符号,而对于 C++ 函数在多数情况下需要在函数名称加上各种前缀或后缀才能作为其标识,比如函数 void Print(int num),如果编译器将其视为 C 语言编译,则该函数在汇编文件中的符号为 Print,若视为 C++,则其符号可能为 Print_int(在 gcc 或 g++ 中函数名称的改变还会考虑命名空间等因素),这也是 C++ 支持函数重载的原因。

(三) 汇编

在该步骤中,编译器将第二步生成的各个文件分别转换为二进制文件,但还不是可执行文件。

(四) 链接

在该步骤中,编译器会为第三步生成的每一个文件“穿针引线”,比如 main() 函数中调用了 Print() 函数,还不知道 Print() 函数在哪里,而在 Print() 函数主体所在的那个文件中,已经标明了 Print() 函数的地址,所以编译器会在 main() 函数中调用 Print() 函数的地方标注 Print() 函数的地址,为程序执行过程中的地址跳转提供目标地址,而编译器能做到这一步的前提,是 main() 函数中 Print() 函数的标识,和 Print() 函数主体所在的那个文件中 Print() 函数的标识是一模一样的,如果不一样,就会触发链接错误。

二、C 与 C++ 接口相互调用的关键

从上文可以得知,要调用一个函数有一个重要条件就是调用处的符号和函数主体处的符号要一模一样,而 C 和 C++ 在编译过程中将函数名称改编成标识符号的方法是不一样的,因此相互调用的关键在于统一接口函数的标识符号,而一般采取的方法是,用 C 函数改编的方法统一接口函数的改编方式。

三、extern "C"

extern "C" 的作用是告诉编译器按 C 函数名称改编的方法将修饰的函数改编成标识符号。extern "C" 一般用在 C++ 文件中。

?
1
2
3
4
5
extern "C" void Print(int num);
extern "C" {
 void Input(int* num);
 void Output(int num);
};

以上是 extern "C" 的两种写法。如此一来,以上三个函数都会按 C 的方式被改编成符号,在 gcc 或 g++ 编译下就会被改变成 Print,Input,Output。

四、C 函数调用 C++ 接口

(一) 调用非成员函数

被调用函数的声明和定义如下。

?
1
2
3
4
5
6
7
8
9
/**
 * called.h
 */
#ifndef CALLED_H
#define CALLED_H
 
extern "C" void PrintCpp(void);
 
#endif
?
1
2
3
4
5
6
7
8
9
10
11
12
/**
 * called.cpp
 */
#include <iostream>
#include "called.h"
 
using namespace std;
 
void
PrintCpp(void) {
 cout << "I\'m cpp." << endl;
}

最终调用如下。

?
1
2
3
4
5
6
7
8
9
10
/**
 * call.c
 */
#include "called.h"
 
int
main(int argc, char const* argv[]) {
 PrintCpp();
 return 0;
}

(二) 调用类成员函数(接口函数没有类指针)

被调用函数声明和定义如下。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * called.h
 */
#ifndef CALLED_H
#define CALLED_H
 
class Console {
public:
 Console();
 virtual void PrintDouble(double num);
};
 
extern "C" void CppPrintDouble(double num);
 
#endif
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * called.cpp
 */
#include <iostream>
#include "called.h"
 
using namespace std;
 
Console::Console() {}
 
void
Console::PrintDouble(double num) {
 cout << num << endl;
}
 
Console* console = new Console();
 
void
CppPrintDouble(double num) {
 console->PrintDouble(num);
}

最终调用如下。

?
1
2
3
4
5
6
7
8
9
10
/**
 * call.c
 */
#include "called.h"
 
int
main(int argc, char const* argv[]) {
 CppPrintDouble(3.14);
 return 0;
}

五、C++ 函数调用 C 接口

被调用函数的声明和定义如下。

?
1
2
3
4
5
6
7
8
9
/**
 * called.h
 */
#ifndef CALLED_H
#define CALLED_H
 
void PrintC(void);
 
#endif
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * called.c
 */
#include <stdio.h>
 
#ifdef __cplusplus
extern "C" {
#endif
#include "called.h"
#ifdef __cplusplus
};
#endif
 
void
PrintC(void) {
 printf("I\'m C.\n");
}

最终调用如下。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * call.cpp
 */
#ifdef __cplusplus
extern "C" {
#endif
#include "called.h"
#ifdef __cplusplus
};
#endif
 
int
main(int argc, char const* argv[]) {
 PrintC();
 return 0;
}

在 called.c 文件中,#ifdef __cplusplus /*...*/ #endif 和 extern "C" 的作用是防止 g++ 编译器对“.c”文件用 C++ 的方式编译,如果用 gcc 进行编译,则直接写 #include "called.h" 就行。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://segmentfault.com/a/1190000022099166
相关文章
  • WPF实现窗体亚克力效果的代码

    WPF实现窗体亚克力效果的代码
    WPF 窗体设置亚克力效果 框架使用大于等于.NET40。 Visual Studio 2022。 项目使用MIT开源许可协议。 WindowAcrylicBlur设置亚克力颜色。 Opacity设置透
  • C#非托管泄漏中HEAP_ENTRY的Size对不上解析

    C#非托管泄漏中HEAP_ENTRY的Size对不上解析
    一:背景 1. 讲故事 前段时间有位朋友在分析他的非托管泄漏时,发现NT堆的_HEAP_ENTRY的 Size 和!heap命令中的 Size 对不上,来咨询是怎么回事?
  • C#中ArrayList 类的使用介绍
    一:ArrayList 类简单说明 动态数组ArrayList类在System.Collecions的命名空间下,所以使用时要加入System.Collecions命名空间,而且ArrayList提供添加,
  • C#使用BinaryFormatter类、ISerializable接口、XmlSeriali

    C#使用BinaryFormatter类、ISerializable接口、XmlSeriali
    序列化是将对象转换成字节流的过程,反序列化是把字节流转换成对象的过程。对象一旦被序列化,就可以把对象状态保存到硬盘的某个位
  • C#序列化与反序列化集合对象并进行版本控制
    当涉及到跨进程甚至是跨域传输数据的时候,我们需要把对象序列化和反序列化。 首先可以使用Serializable特性。 1 2 3 4 5 6 7 8 9 10 11 12 13 14
  • C#事件中关于sender的用法解读

    C#事件中关于sender的用法解读
    C#事件sender的小用法 开WPF新坑了,看了WPF的炫酷界面,再看看winForm实在是有些惨不忍睹(逃)。后面会开始写一些短的学习笔记。 一、什么
  • 在C#程序中注入恶意DLL的方法

    在C#程序中注入恶意DLL的方法
    一、背景 前段时间在训练营上课的时候就有朋友提到一个问题,为什么 Windbg 附加到 C# 程序后,程序就处于中断状态了?它到底是如何实现
  • 基于C#实现一个简单的FTP操作工具
    实现功能 实现使用FTP上传、下载、重命名、刷新、删除功能 开发环境 开发工具: Visual Studio 2013 .NET Framework版本:4.5 实现代码 1 2 3 4 5 6 7
  • C#仿QQ实现简单的截图功能

    C#仿QQ实现简单的截图功能
    接上一篇写的截取电脑屏幕,我们在原来的基础上加一个选择区域的功能,实现自定义选择截图。 个人比较懒,上一篇的代码就不重新设计
  • C#实现线性查找算法的介绍
    线性查找,肯定是以线性的方式,在集合或数组中查找某个元素。 通过代码来理解线性查找 什么叫线性?还是在代码中体会吧。 首先需要一
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计