我遇到了一段工作代码(使用 XLC8 和 MSFT9 编译器),其中包含一个 C++ 文件,其中包含使用 C 链接和引用参数定义的函数。这让我很烦恼,因为参考文献仅限于 C++。该函数是从 C 代码中调用的,其中它被声明为采用相同类型的指针参数来代替引用参数。
简化示例:
C++ 文件:
extern "C" void f(int &i)
{
i++;
}
C 文件:
void f(int *);
int main()
{
int a = 2;
f(&a);
printf("%d\n", a); /* Prints 3 */
}
现在,坊间流传的说法是,大多数 C++ 编译器在底层都像指针一样实现引用。是不是就是这样,并且纯粹是运气使这段代码起作用,还是在 C++ 规范中的某个地方说明了当您定义带有引用参数和 C 链接的函数时的结果是什么?我没找到这方面的信息。
在许多情况下(但不是全部),可以使用“自动取消引用”指针来实现引用。 C++ 标准中不保证任何特定编译器以这种方式处理具有 C 链接和引用参数的函数,您应该将其视为实现细节。
如果您需要在不依赖实现细节的情况下执行此操作,那么编写一个接受指针并调用您的函数的转发函数并不难:
void real_f(int& n) {
n++;
}
extern "C" void f(int* p) { // called from C code
real_f(*p);
}
我的 n3000.pdf 副本(来自此处),在第 7.5 节中有这样的说法—链接规范:
9。从 C++ 到中定义的对象的链接 其他语言和定义的对象 在其他语言的 C++ 中是 实现定义的和 依赖于语言。仅当 两种对象布局策略 语言实现类似 实现这样的联动就足够了。
由于 C 和 C++ 是不同的语言,这意味着你不能依赖通用编译器的这个“功能”。
同一节中的注释 5 更强(强调我的):
如果两个声明声明了函数 具有相同的名字和 参数类型列表(8.3.5)为 同一命名空间的成员或 声明具有相同名称的对象 是同一命名空间的成员并且 声明给出了名称 不同语言之间的联系, 程序格式不正确; 无诊断 如果出现声明则需要 在不同的翻译单位.
因此,我想说,您所做的并不能保证按照标准工作,并且编译器不需要为您给出的示例打印诊断信息,因为声明位于不同的翻译单元中。
仅供参考,它在 Snow Leopard 上使用 gcc 和 g++ 4.2.1 版本“对我有用”。
我认为您已经得到了一些很好的答案,因此只需指出您问题上的一些额外内容即可。
我的粗浅理解是 extern 只是创建一个 C 符号。
所以即使我添加一个类对象,你的示例仍然“似乎有效”(gcc/g++) - 我必须尝试相信它:
class A {};
extern "C" void f(int &i, A a) {
i++;
}
A reference 是对象的替代名称。从技术上讲,C 中没有任何内容可以直接映射到 C++ 引用。
引用的明显实现是作为一个(常量)指针,每次使用时都会取消引用。 因此,根据编译器实现引用的方式,您的代码可能会起作用。但这是不正确的。
解决方案是编写一个 C 函数,该函数接收真实对象或指向该对象的指针,并从中调用 C++ 函数。
这就是 Bjarne Stroustrup 关于参考文献的说法:
“大多数引用都是使用指针变量实现的;引用通常占用一个内存字。但是,纯粹本地使用的引用可以(而且经常)被优化器消除”。
例如:
struct S
{
int a;
int b[100];
}; // just an example
void do_something(const vector<S>& v)
{
for (int i=0; i<v.size(); ++i)
{
int(&p)[100] = v[i].b;
for (int j=0; j<100; ++j)
cout <<p[j];
}
}
在这种情况下,p不需要存储在内存中(也许它只存在于寄存器中,也许它消失在指令中)。