235 lines
24 KiB
HTML
235 lines
24 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh" dir="ltr" class="client-nojs" xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh">
|
|
<head><meta http-equiv="x-ua-compatible" content="ie=edge">
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<title>语言链接</title>
|
|
<link rel="stylesheet" href="ext.css" />
|
|
<link rel="stylesheet" href="site_modules.css" />
|
|
</head>
|
|
<body class="mediawiki ltr sitedir-ltr ns-0 ns-subject page-cpp_language_language_linkage skin-cppreference2 action-view cpp-navbar">
|
|
<div id="cpp-content-base">
|
|
<div id="content"><a id="top"></a>
|
|
<h1 id="firstHeading" class="firstHeading">语言链接</h1>
|
|
<div id="bodyContent">
|
|
<div id="contentSub"><span class="subpages">< <a href="cpp.html">cpp</a>‎ | <a href="cpp-language.html">language</a></span></div>
|
|
<div id="mw-content-text" lang="zh" dir="ltr" class="mw-content-ltr" xml:lang="zh">
|
|
<p>提供以不同程序语言编写的模块间的链接。</p>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>extern</b></code> <span class="t-spar">字符串字面量</span> <code><b>{</b></code> <span class="t-spar">声明序列</span><span class="t-mark">(可选)</span> <code><b>}</b></code></td>
|
|
<td>(1)</td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><code><b>extern</b></code> <span class="t-spar">字符串字面量</span> <span class="t-spar">声明</span></td>
|
|
<td>(2)</td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<div class="t-li1"><span class="t-li">1)</span> 将语言说明 <span class="t-spar">字符串字面量</span> 应用到声明于 <span class="t-spar">声明序列</span> 中的所有函数类型,具有外部链接的函数名,和具有外部链接的变量。</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 将语言说明 <span class="t-spar">字符串字面量</span> 应用到单一声明或定义。</div>
|
|
<table class="t-par-begin">
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">字符串字面量</span></td>
|
|
<td>-</td>
|
|
<td>所要求的语言链接的名字</td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">声明序列</span></td>
|
|
<td>-</td>
|
|
<td>声明的序列,可以包含嵌套的链接说明</td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">声明</span></td>
|
|
<td>-</td>
|
|
<td>一个声明</td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E8.A7.A3.E9.87.8A">解释</span></h3>
|
|
<p>所有函数类型,所有拥有<a href="cpp-language-storage_duration.html">外部链接</a>的函数名,及所有拥有<a href="cpp-language-storage_duration.html">外部链接</a>的变量名,拥有一种称作<i>语言链接</i>的性质。语言链接封装与以另一程序语言编写的模块进行链接的要求的集合:<a href="https://en.wikipedia.org/wiki/Calling_convention" class="extiw" title="enwiki:Calling convention">调用约定</a>、<a href="https://en.wikipedia.org/wiki/Name_mangling" class="extiw" title="enwiki:Name mangling">名字重整</a>的算法,等等。</p>
|
|
<p>只有以下两种语言链接保证受支持:</p>
|
|
<ol>
|
|
<li><span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="st0">"C++"</span></span></span>,默认的语言链接。</li>
|
|
<li><span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="st0">"C"</span></span></span>,使得以 C 程序语言编写的函数进行链接,以及在 C++ 程序中定义能从 C 模块调用的函数成为可能。</li>
|
|
</ol>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">extern</span> <span class="st0">"C"</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw4">int</span> open<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">char</span> <span class="sy2">*</span>pathname, <span class="kw4">int</span> flags<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// C 函数声明</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw4">int</span> fd <span class="sy1">=</span> open<span class="br0">(</span><span class="st0">"test.txt"</span>, <span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 从 C++ 程序调用 C 函数</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="co1">// 此 C++ 函数能从 C 代码调用</span>
|
|
<span class="kw4">extern</span> <span class="st0">"C"</span> <span class="kw4">void</span> handler<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"Callback invoked<span class="es1">\n</span>"</span><span class="sy4">;</span> <span class="co1">// 它能使用 C++</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>因为语言链接是每个函数类型的一部分,所以函数指针也要维持语言链接。函数类型的语言链接(表示调用约定)和函数名的语言链接(表示名字重整)是彼此独立的:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">extern</span> <span class="st0">"C"</span> <span class="kw4">void</span> f1<span class="br0">(</span><span class="kw4">void</span><span class="br0">(</span><span class="sy2">*</span>pf<span class="br0">)</span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 声明一个具有 C 链接的函数 f1,</span>
|
|
<span class="co1">// 它返回 void 并接受一个指向返回 void 且不接受形参的 C 函数的指针</span>
|
|
|
|
<span class="kw4">extern</span> <span class="st0">"C"</span> <span class="kw1">typedef</span> <span class="kw4">void</span> FUNC<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 声明 FUNC 为返回 void 且不接受形参的 C 函数类型</span>
|
|
|
|
FUNC f2<span class="sy4">;</span> <span class="co1">// 名字 f2 拥有 C++ 链接,但其类型是 C 函数</span>
|
|
<span class="kw4">extern</span> <span class="st0">"C"</span> FUNC f3<span class="sy4">;</span> <span class="co1">// 名字 f3 拥有 C 链接且其类型是 C 函数 void(void)</span>
|
|
<span class="kw4">void</span> <span class="br0">(</span><span class="sy2">*</span>pf2<span class="br0">)</span><span class="br0">(</span>FUNC<span class="sy2">*</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 名字 pf2 拥有 C++ 链接,且其类型是“指向返回 void 并接受</span>
|
|
<span class="co1">// 一个‘指向返回 void 且不接受形参的 C 函数的指针’的 C++ 函数的指针</span>
|
|
|
|
<span class="kw4">extern</span> <span class="st0">"C"</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw4">static</span> <span class="kw4">void</span> f4<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 函数 f4 的名字拥有内部链接(无语言)</span>
|
|
<span class="co1">// 但函数的类型拥有 C 语言链接</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>在同一命名空间中具有相同名字和相同形参列表的两个函数不能拥有两个不同的语言链接(然而需要注意,形参的链接可容许这种重载,例如 <span class="t-lc"><a href="cpp-algorithm-qsort.html">std::qsort</a></span> 和 <span class="t-lc"><a href="cpp-algorithm-bsearch.html">std::bsearch</a></span> 的情况)。类似地,同一命名空间中的两个变量不能拥有两种不同的语言链接。</p>
|
|
<h4><span class="mw-headline" id=".22C.22_.E9.93.BE.E6.8E.A5.E7.9A.84.E7.89.B9.E6.AE.8A.E8.A7.84.E5.88.99">"C" 链接的特殊规则</span></h4>
|
|
<ol>
|
|
<li>当类成员的声明和成员函数类型的声明出现于 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="st0">"C"</span></span></span> 语言块中时,其链接仍保持为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="st0">"C++"</span></span></span>。</li>
|
|
<li>当两个拥有同一无限定名字的函数声明于不同命名空间,且都拥有 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="st0">"C"</span></span></span> 语言链接时,两个声明表示同一函数。</li>
|
|
<li>当两个拥有 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="st0">"C"</span></span></span> 语言链接和相同名字的变量出现于不同命名空间时,它们表示同一变量。</li>
|
|
<li>一个 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="st0">"C"</span></span></span> 变量和一个 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="st0">"C"</span></span></span> 函数不能拥有相同的名字,与它们是否定义于同一命名空间无关。</li>
|
|
</ol>
|
|
<h3><span class="mw-headline" id=".E6.B3.A8.E8.A7.A3">注解</span></h3>
|
|
<p>语言链接只能在<a href="cpp-language-scope.html#.E5.91.BD.E5.90.8D.E7.A9.BA.E9.97.B4.E4.BD.9C.E7.94.A8.E5.9F.9F">命名空间作用域</a>出现。</p>
|
|
<p>语言说明的花括号不建立作用域。</p>
|
|
<p>当语言说明发生嵌套时,只有最内层的说明会生效。</p>
|
|
<p>函数可以在带语言说明的声明之后,进行不带链接说明的重声明,第二个声明将重复使用首个声明的语言链接。反之则不行:如果首个声明无语言链接,那么链接会被假定为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="st0">"C++"</span></span></span>,而以其他语言链接重声明则会导致错误。</p>
|
|
<p>直接包含于语言链接说明之中的声明,被处理为如同它含有 <a href="cpp-language-storage_duration.html"><code>extern</code> 说明符</a>,用以确定所声明的名字的<a href="cpp-language-storage_duration.html#.E9.93.BE.E6.8E.A5">链接</a>以及它是否为<a href="cpp-language-definition.html">定义</a>。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">extern</span> <span class="st0">"C"</span> <span class="kw4">int</span> x<span class="sy4">;</span> <span class="co1">// 声明且非定义</span>
|
|
<span class="co1">// 上一行等价于 extern "C" { extern int x; }</span>
|
|
|
|
<span class="kw4">extern</span> <span class="st0">"C"</span> <span class="br0">{</span> <span class="kw4">int</span> x<span class="sy4">;</span> <span class="br0">}</span> <span class="co1">// 声明及定义</span>
|
|
|
|
<span class="kw4">extern</span> <span class="st0">"C"</span> <span class="kw4">double</span> f<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">static</span> <span class="kw4">double</span> f<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:链接冲突</span>
|
|
|
|
<span class="kw4">extern</span> <span class="st0">"C"</span> <span class="kw4">static</span> <span class="kw4">void</span> g<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:链接冲突</span></pre></div>
|
|
</div>
|
|
<p><code>extern "C"</code> 允许 C++ 程序中包含(include)含有 C 库函数的声明的头文件,但如果与 C 程序共用相同的头文件,就必须以适当的 <a href="cpp-preprocessor-conditional.html"><tt>#ifdef</tt></a> 隐藏 <code>extern "C"</code>(C 中不允许使用),通常为 <a href="cpp-preprocessor-replace.html#.E9.A2.84.E5.AE.9A.E4.B9.89.E5.AE.8F"><tt>__cplusplus</tt></a>:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co2">#ifdef __cplusplus</span>
|
|
<span class="kw4">extern</span> <span class="st0">"C"</span> <span class="kw4">int</span> foo<span class="br0">(</span><span class="kw4">int</span>, <span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// C++ 编译器所见</span>
|
|
<span class="co2">#else</span>
|
|
<span class="kw4">int</span> foo<span class="br0">(</span><span class="kw4">int</span>, <span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// C 编译器所见</span>
|
|
<span class="co2">#endif</span></pre></div>
|
|
</div>
|
|
<p>在现代编译器中,只有 Oracle Studio 区分带 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="st0">"C"</span></span></span> 与 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="st0">"C++"</span></span></span> 语言链接的函数类型,其他编译器不允许只在语言链接有区别的重载,包括 C++ 标准要求的重载集( <span class="t-lc"><a href="cpp-algorithm-qsort.html">std::qsort</a></span>、 <span class="t-lc"><a href="cpp-algorithm-bsearch.html">std::bsearch</a></span>、 <span class="t-lc"><a href="cpp-utility-program-signal.html">std::signal</a></span>、 <span class="t-lc"><a href="cpp-utility-program-atexit.html">std::atexit</a></span> 及 <span class="t-lc"><a href="cpp-utility-program-at_quick_exit.html">std::at_quick_exit</a></span> ): <a rel="nofollow" class="external text" href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316">GCC bug 2316</a> 、 <a rel="nofollow" class="external text" href="https://bugs.llvm.org/show_bug.cgi?id=6277">Clang bug 6277</a> 、 <a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue1555">CWG1555</a> 。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">extern</span> <span class="st0">"C"</span> <span class="kw1">using</span> c_predfun <span class="sy1">=</span> <span class="kw4">int</span><span class="br0">(</span><span class="kw4">const</span> <span class="kw4">void</span><span class="sy2">*</span>, <span class="kw4">const</span> <span class="kw4">void</span><span class="sy2">*</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">extern</span> <span class="st0">"C++"</span> <span class="kw1">using</span> cpp_predfun <span class="sy1">=</span> <span class="kw4">int</span><span class="br0">(</span><span class="kw4">const</span> <span class="kw4">void</span><span class="sy2">*</span>, <span class="kw4">const</span> <span class="kw4">void</span><span class="sy2">*</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 非良构,但为大多数编译器接受</span>
|
|
static_assert<span class="br0">(</span><a href="cpp-types-is_same.html"><span class="kw588">std::<span class="me2">is_same</span></span></a><span class="sy1"><</span>c_predfun, cpp_predfun<span class="sy1">></span><span class="sy4">::</span><span class="me2">value</span>,
|
|
<span class="st0">"C 和 C++ 语言链接不应区分函数类型。"</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 多数编译器中下列声明不声明重载</span>
|
|
<span class="co1">// 因为 c_predfun 与 cpp_predfun 被认为是同一类型</span>
|
|
<span class="kw4">void</span> qsort<span class="br0">(</span><span class="kw4">void</span><span class="sy2">*</span> base, <a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> nmemb, <a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> size, c_predfun<span class="sy2">*</span> compar<span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">void</span> qsort<span class="br0">(</span><span class="kw4">void</span><span class="sy2">*</span> base, <a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> nmemb, <a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> size, cpp_predfun<span class="sy2">*</span> compar<span class="br0">)</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id="D.E7.BC.BA.E9.99.B7.E6.8A.A5.E5.91.8A">D缺陷报告</span></h3>
|
|
<p>下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。</p>
|
|
<table class="dsctable" style="font-size:0.8em">
|
|
<tr>
|
|
<th>DR</th>
|
|
<th>应用于</th>
|
|
<th>出版时的行为</th>
|
|
<th>正确行为</th>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue4">CWG 4</a></td>
|
|
<td>C++98</td>
|
|
<td>具有内部链接的名字可以有语言链接</td>
|
|
<td>只有具有外部链接的名字才可以</td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E5.BC.95.E7.94.A8">引用</span></h3>
|
|
<div class="t-ref-std-c++20">
|
|
<ul>
|
|
<li>C++20 标准(ISO/IEC 14882:2020):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>9.11 Linkage specifications [dcl.link]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++17">
|
|
<ul>
|
|
<li>C++17 标准(ISO/IEC 14882:2017):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>10.5 Linkage specifications [dcl.link]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++14">
|
|
<ul>
|
|
<li>C++14 标准(ISO/IEC 14882:2014):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>7.5 Linkage specifications [dcl.link]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++11">
|
|
<ul>
|
|
<li>C++11 标准(ISO/IEC 14882:2011):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>7.5 Linkage specifications [dcl.link]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++03">
|
|
<ul>
|
|
<li>C++03 标准(ISO/IEC 14882:2003):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>7.5 Linkage specifications [dcl.link]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|