惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Register - Security
The Register - Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
MyScale Blog
MyScale Blog
V
Visual Studio Blog
云风的 BLOG
云风的 BLOG
aimingoo的专栏
aimingoo的专栏
C
Check Point Blog
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏
L
LangChain Blog
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Security @ Cisco Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
人人都是产品经理
人人都是产品经理
H
Hacker News: Front Page
L
Lohrmann on Cybersecurity
T
Troy Hunt's Blog
T
Threat Research - Cisco Blogs
A
About on SuperTechFans
T
Threatpost
AWS News Blog
AWS News Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
Tor Project blog
Google Online Security Blog
Google Online Security Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tenable Blog
W
WeLiveSecurity
博客园 - 叶小钗
K
Kaspersky official blog
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
Hugging Face - Blog
Hugging Face - Blog
M
MIT News - Artificial intelligence
Hacker News - Newest:
Hacker News - Newest: "LLM"
Engineering at Meta
Engineering at Meta
有赞技术团队
有赞技术团队
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Secure Thoughts
小众软件
小众软件
D
Docker
爱范儿
爱范儿
C
Cyber Attacks, Cyber Crime and Cyber Security
N
News and Events Feed by Topic
S
Schneier on Security
博客园 - 三生石上(FineUI控件)
D
DataBreaches.Net

嵌入式工程猫的博客

使用树莓派 4 和 Moonlight 串流游戏的实践 维护 Nginx 时,什么时候应该用 reload,什么时候应该用 restart? 批量修改 qbittorrent-nox 内种子的 tracker 地址 把 vim 的缩进设为 4 个字符,并且 tab 自动转空格 不用 snap,在 Ubuntu 上安装 certbot 在 Ubuntu 中启用 swap - 嵌入式工程猫的博客 让 Nginx 反向代理的程序获取客户端真实 IP - 嵌入式工程猫的博客 在 Linux 中显示所有正在监听的 TCP 端口 航模舵机控制及其 PWM 调制的进一步理解 - 嵌入式工程猫的博客 如何自签名带 SAN 字段的 SSL/TLS 证书 深入理解以太网网线原理 - 嵌入式工程猫的博客 FreeBSD vs Linux:哪个开源操作系统更强大 - 嵌入式工程猫的博客 如何在 Markdown 中修改字体颜色 - 嵌入式工程猫的博客 如何在 ESP8266 上选用合适的引脚 - 嵌入式工程猫的博客 如何 DIY 一个苏康码与行程码“双码合一”的健康码 APP - 嵌入式工程猫的博客 我两周就写了三行代码 - ARM Cortex A9 中断与浮点数运算、FPU 问题 随便聊聊最近在本站折腾的那些东西 - 嵌入式工程猫的博客 分享一下我的家庭网络布局 - 嵌入式工程猫的博客 Mapuino - 一个硬件极客风的 WEB 访客地图显示摆件 Topuino - 你愿意在办公桌上放一个监控服务器的小摆件吗? - 嵌入式工程猫的博客
再一次理解 C++ 中的 extern "C"
2023-07-03 · via 嵌入式工程猫的博客

本文是“攻玉计划”的一部分,翻译自 https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c 中 Ciro Santilli 的回答

main.cpp

1
2
3
4
5
6
7
8
9
10
void f() {}
void g();

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


void h() { g(); eg(); }

将上述代码编译为 ELF 格式的二进制,然后反汇编:

1
2
g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o

摘取一部分输出:

1
2
3
4
5
6
 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 _GLOBAL_OFFSET_TABLE_
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv
13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg

可见:

  • efeg 在符号表中的名称与其在原先代码中的名称一致
  • 其他符号都被修饰过了,我们可以用 c++filt 工具还原其本来的样子:
1
2
3
4
5
6
$ c++filt _Z1fv
f()
$ c++filt _Z1hv
h()
$ c++filt _Z1gv
g()

所以,在以下两种情况下,我们需要使用 extern "C"

  • 在 C++ 中调用 C 代码:告诉 g++ 可能会遇到由 gcc 生成的未修饰过的符号名
  • 在 C 中调用 C++ 代码:让 g++ 生成未修饰的符号名,以供 gcc 调用

某些不能在 extern “C” 中使用的代码

显然,任何需要使用 C++ 名称修饰的语言特性,都不能写在 extern "C" 中:

1
2
3
4
5
6
7
8
9
10
extern "C" {


void f();
void f(int i);



template <class C> void f(C i) { }
}

在 C++ 中调用 C 代码的最小可运行代码样例

在 C++ 中调用 C 代码很简单:每个 C 函数都只有一个未修饰的符号名,所以不需要额外的操作。

main.cpp

1
2
3
4
5
6
7
#include <cassert>

#include "c.h"

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

c.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef C_H
#define C_H



#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif

#endif

c.c

1
2
3
#include "c.h"

int f(void) { return 1; }

运行:

1
2
3
4
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

如果没有 extern "C" 的话,链接器会报错:

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

因为 g++ 会寻找一个修饰过的 f,但是 gcc 并不会编译出修饰过的符号名。

在 C 中调用 C++ 代码的最小可运行代码样例

在 C 中调用 C++ 代码稍微困难一点:我们需要手动管理所有暴露给 C 的函数接口,并且使它们在编译时不被修饰。

代码如下:

main.c

1
2
3
4
5
6
7
8
9
#include <assert.h>

#include "cpp.h"

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

cpp.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef CPP_H
#define CPP_H

#ifdef __cplusplus

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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);
}

运行:

1
2
3
4
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

如果不加 extern "C" 的话,会报错:

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

因为 g++ 会生成修饰后的符号名,但 gcc 无法理解。

当我在 C++ 中包含标准库 C 头文件的时候,extern “C” 在哪?