34.extern C的作用


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


externC

1.为什么要使用externC

一句话总结,C++支持函数重载,C语言不支持函数重载,在生成的C++编译文件中函数名会根据参数进行混淆(mangle),而C语言的编译文件不会被混淆,所以在C++程序中链接C语言的函数动态库时需要使用externC来保证函数签名的正确性。

/// main.cpp

void f() {}
void g();

extern "C" {
    void ef() {}
    void eg();
}

/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }

如上代码,使用命令编译后查看目标文件符号:

g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o
# Symbol table '.symtab' contains 13 entries:
#    Num:    Value          Size Type    Bind   Vis      Ndx Name
#      0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
#      1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test_externC.cc
#      2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
#      3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
#      4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
#      5: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
#      6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
#      7: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
#      8: 0000000000000000     7 FUNC    GLOBAL DEFAULT    1 _Z1fv
#      9: 0000000000000007     7 FUNC    GLOBAL DEFAULT    1 ef
#     10: 000000000000000e    17 FUNC    GLOBAL DEFAULT    1 _Z1hv
#     11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
#     12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND eg

可以看到被extern "C"修饰的函数ef/gf函数名没有被混淆mangle,而未被函数名包围的按C++的编译规则被混淆了。

使用c++filt _Z1fv命令可以反混淆(unmangle)函数名称,会发现_Z1fv/_Z1hv/_Z1gv对应的就是f/g/h

c++filt是一个用于解码(反编译)低级别的C++Java符号名称的工具,用于将修饰后的函数名映射回源函数名。

使用externC的一个例子

先创建一个C语言的so库:

// add.h

int add(int x, int y);

// add.c

#include <add.h>

int add(int x, int y)
{
    return x + y;
}
/// 编译成so库
/// gcc -fPIC -shared add.c -o libadd.so

通过上面的方式编译的so文件中存在的函数符号名为add,不包含参数名称修饰,因此无法直接被c++程序链接

// main.cc

#include <add.h>

extern "C" {
    printf(fmt, ...);
}

int main(int argc, char** argv)
{
    int i = 1, j = 10;
    printf("%d\n", add(i, j));
    return 0;
}
/// gcc main.cc -o et -I ./ -L ./ -ladd
/// 报错
/// /usr/bin/ld: /tmp/ccxcRKM6.o: in function `main':
/// main.cc:(.text+0x2f): undefined reference to `add(int, int)'
/// collect2: error: ld returned 1 exit status

要想在c++中使用C语言编译的库,需要在C语言库的头文件中告诉C++哪些代码需要使用C的方式来编译,上面例子将add.h文件改成如下格式即可:

/// add.h
#ifdef __cplusplus
extern "C" {
#endif
    int add(int x, int y);

#ifdef __cplusplus
}
#endif

如此就能正常编译运行了。

reference

1.https://www.geeksforgeeks.org/extern-c-in-c/
2.https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
extern "C"的作用是用于实现C和C++之间的相互调用问题。C和C++对于函数名的处理方式是不一样的,所以当我们希望调用其他用C编写的函数时,需要告诉编译器使用不同的要求。通过在C++代码中使用extern "C"来声明函数,可以使编译器按照C的规则来处理函数名和参数排列的顺序,从而实现C和C++之间的兼容调用。 需要注意的是,extern "C"不能在函数内部使用,而是用于函数的声明。如果一个函数有多个声明,可以都加上extern "C",或者只在第一次声明中出现,后面的声明会接受第一个链接指示符的规则。 所以当我们需要在C++代码中调用C函数时,可以使用extern "C"来实现对C函数的调用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【004 关键字】extern “C“的作用是什么?](https://blog.csdn.net/qq_41709234/article/details/123082378)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [extern关键字作用.docx](https://download.csdn.net/download/Stephen___Qin/87567249)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值