首页 > 其他分享 >extern "c"的用法

extern "c"的用法

时间:2024-10-24 16:10:18浏览次数:1  
标签:函数 int c++ 用法 C++ extern main

在我的印象里,extern "c"一直是c++调用c接口用的,用法是用exetrn "c"把相应写好的c接口包住。

即在 C++ 出现以前,很多代码都是 C 语言写的,而且很底层的库也是 C 语言写的,为了更好的支持原来的 C 代码和已经写好的 C 语言库,需要在 C++ 中尽可能的支持 C ,而 extern “C” 就是其中的一个策略。 extern “C” 主要作用就是为了能够正确实现 C++ 代码调用其他 C 语言代码。 extern “C” 会指示编译器这部分代码按 C 语言的进行编译,而不是 C++。

但是在最近的工作中同事提到c调用c++时同样用到extern "c"语法,惊讶之余把这块内容好好看了一下,有了很多收获:

 

1.到底什么是extern "c"

理解这个问题的关键在于函数重载: c++中支持函数重载,c语言不支持函数重载,这造成了c++和c语言的函数名解析不同。c语言函数名就是函数名,c++的函数名是函数名+参数组合起来的。

C语言的函数名称解析仅仅基于函数名称;而C++的函数名称解析基于函数名称和参数列表。

比如,函数void func(double a) 在CC++中的编译阶段函数名称会被解析成什么呢?

C语言中,由于没有名称修饰,所以在编译时函数名称仍然是func,不会因为参数类型或数量而改变。

C++中,由于名称修饰的存在,函数名称在编译阶段会被编译器转换成一个包含函数原型信息的唯一标识符。通常会涉及函数返回类型、参数类型以及参数数量。以GCC(GNU Compiler Collection)为例,func(double a)会被转换成_Z4funcd ,这里:

  • _Z:是GCC用来表示修饰名称的前缀
  • 4:表示函数名称func的的字符数
  • d:是double类型的编码

因此,用c++的方式去寻找c语言的符号是无法寻找到的。extern "C"为何可以做到?

extern "C"的作用就是修改了符号表的生成方式,将c++符号的生成方式换成了c的生成方式。

c库中生成的符号是c编译器的符号, 因此c语言可以直接链接。而c++程序需要使用extern "C"让编译器使用c的符号命名方式去进行链接,这样才能找到对应的符号。

 

2.extern "c"到底是用来c++调用c,还是c

直接说答案:都可以。

 

3.c++ call c和 c call c++的实战

3.1Minimal runnable C from C++ example

main.cpp

#include <cassert>

#include "c.h"

int main() {
    assert(f() == 1);
}

c.h

#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++ 
 * because C does not know what this extern "C" thing is. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif

#endif

c.c

#include "c.h"

int f(void) { return 1; }

run

g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out

Without extern "C" the link fails with:

main.cpp:6: undefined reference to `f()'

 

3.2Minimal runnable C++ from C example

main.c

#include <assert.h>

#include "cpp.h"

int main(void) {
    assert(f_int(1) == 2);
    assert(f_float(1.0) == 3);
    return 0;
}

cpp.h

#ifndef CPP_H
#define CPP_H

#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif

#endif

cpp.cpp

#include "cpp.h"

int f(int i) {
    return i + 1;
}

int f(float i) {
    return i + 2;
}

int f_int(int i) {
    return f(i);
}

int f_float(float i) {
    return f(i);
}

run

gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out

Without extern "C" it fails with:

main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'

 

分析:着重看c调用c++,这里巧妙利用了#ifdef __cplusplus,使得当用cpp编译时,函数还是f。当没用c++编译时,函数就变成了f_int和f_float。这里的f_int和f_float就是调用了f。

这样就同时满足了c和c++调用该函数的需求,我即可以在c++中调用该接口,也可以在c语言中调用该接口。

注意:如果编译时直接用命令“g++ main.c cpp.cpp”则不会报错,即使没有用extern "C"。这是因为C++是兼容C的语法的,如果在编译的时候,c文件就是用g++编译的,则函数的命名规则会变得跟cpp文件中的函数一样,那么肯定不会报错了

 

4 进阶:C语言调用c++中的成员函数

在C程序中调用C++成员函数需要遵循几个步骤:

  1. 确保C++成员函数是可以从C访问的,即它必须是extern "C"的,这样它会使用C的链接方式。

  2. 使用C++的extern "C"声明来导出函数,以便C可以链接到它。

  3. 确保C++对象实例的存在,并且在C中正确地通过函数指针调用成员函数。

     

// myclass.h
class MyClass {
public:
    void myFunction();
    static void myStaticFunction();
};
 
extern "C" void MyClass_myFunction(MyClass* obj);
extern "C" void MyClass_myStaticFunction();
// myclass.cpp
#include "myclass.h"
 
void MyClass::myFunction() {
    // 实现
}
 
void MyClass::myStaticFunction() {
    // 实现
}
 
extern "C" void MyClass_myFunction(MyClass* obj) {
    obj->myFunction();
}
 
extern "C" void MyClass_myStaticFunction() {
    MyClass::myStaticFunction();
}
// main.c
#include "myclass.h"
 
int main() {
    // 调用静态成员函数
    MyClass_myStaticFunction();
 
    // 调用非静态成员函数
    MyClass obj;
    MyClass_myFunction(&obj);
 
    return 0;
}

C++类MyClass有一个非静态成员函数myFunction()和一个静态成员函数myStaticFunction()。通过定义额外的extern "C"函数,我们可以从C中调用这些成员函数。注意,对于非静态成员函数,你需要一个MyClass实例,并通过C函数指针调用它的成员函数。

 

参考链接:

  • What is the effect of extern "C" in C++?   https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c 
  • extern "C"如何使用?  https://zhuanlan.zhihu.com/p/709910309  
  • c++中的extern ”C“  https://www.zhihu.com/tardis/bd/art/634091433?source_id=1001
  • c语言和c++的相互调用  https://blog.csdn.net/qq_29344757/article/details/73332501

 

标签:函数,int,c++,用法,C++,extern,main
From: https://www.cnblogs.com/ArsenalfanInECNU/p/18499811

相关文章

  • ProtoStuff用法
    packagecom.shuwei.dai;importjava.io.ByteArrayInputStream;importjava.io.ByteArrayOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;/***jdk序列化*/publicclassJdkSerializeUtil{/***序列化**......
  • C++ STL基本用法概述(简洁版)
    vector变长数组,倍增思想基本函数 size()   //返回元素个数,时间复杂度为o(1)empty()   //返回a是否为空,时间复杂度为o(1)clear()   //清空front()/back()   //返回第一个数/最后一个数push_back()   //最后插入一个数pop_back()   //删掉最后一个数......
  • Java中super和this的用法和区别
    一、this是自身的一个对象,代表对象本身,可以理解为指向对象本身的一个指针this是用来访问本类实例属性和方法的,它会先从本类中找,如果本类中找不到则在父类中找this属性使用:用来赋值本类属性(最常见的用法),写类时的setter方法eg:publicvoidsetId(intid){this.id=i......
  • 「深度解析」向量召回和字面召回用法和组合
        上面图中的5个问题,是每个使用大模型进行知识库搭建的时候都要遇到问题,这些问题基本上就决定了问答答案的准确性,本篇文章我们先不讨论上面五个问题的解决方案,我们今天重点讨论两种召回方式:字面召回和向量召回, 字面和向量召回都是搜索召回中的重要方法,如何更好地权......
  • Scala的trait的用法
    ①只有抽象方法的trait②只有抽象成员和方法的trait③具体成员的变量和方法④对象继承特质例子://定义一个特质//特质可以有抽象属性,具体属性,抽象方法,具体方法traitBeauty{//抽象方法valhair:String//具体属性varage=18valheight=180//val不......
  • require.context 用法
    require.context是Webpack特有的一个API,它用于动态加载模块,特别是在需要批量引入文件时非常有用。它允许你创建一个上下文,来包含特定目录下的模块,并可以以编程方式导入它们。以下是require.context的用法及示例。基本语法constcontext=require.context(directory,useS......
  • curl,nc和telnet的用法以及其他常用工具(nc代理与重定向)
      curl 是一个功能强大的命令行工具和库,用于在网络上传输数据。它支持多种协议,包括HTTP、HTTPS、FTP、FTPS、SFTP、SMTP、POP3、IMAP等。curl 的名称来源于“ClientforURLs”。下面是对 curl 工具的详细介绍,包括基本用法、常用选项以及实际应用场景。基本功能发送......
  • 【SHELL】awk 命令用法
    linux命令行查询awk用法信息awk--helpUsage:awk[POSIXorGNUstyleoptions]-fprogfile[--]file...Usage:awk[POSIXorGNUstyleoptions][--]'program'file...POSIXoptions:GNUlongoptions:(standard)-fprogfile......
  • 【SHELL】sed 命令用法
    linux命令行查询grep用法信息sed--helpUsage:sed[OPTION]...{script-only-if-no-other-script}[input-file]...-n,--quiet,--silentsuppressautomaticprintingofpatternspace--debugannotateprogramexecution......
  • Java中super关键词的用法和注意事项
    在Java中,super关键字用于引用当前对象的父类。它主要有以下几种用途:1.访问父类的属性和方法:当子类中定义了与父类同名的属性或方法时,可以使用super关键字来明确指出要访问的是父类中的属性或方法。2.调用父类的构造器:在子类的构造方法中,可以使用super()来显式调用父类的构造器,以......