【问题标题】:C++ by-reference argument and C linkageC++ 引用参数和 C 链接
【发布时间】:2009-12-15 08:40:02
【问题描述】:

我遇到了一段工作(使用 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++ 标准(链接规范)的第 7.5 节对此没有任何说明。

标签: c++ c pointers compiler-construction reference


【解决方案1】:

在许多情况下,但不是全部情况下,可以使用“自动取消引用”指针来实现引用。 C++ 标准不保证任何特定编译器都会以这种方式处理具有 C 链接和引用参数的函数,您应该将其视为实现细节。

如果您需要在不依赖实现细节的情况下执行此操作,那么编写一个接收指针并调用您的函数的转发函数并不难:

void real_f(int& n) {
  n++;
}
extern "C" void f(int* p) { // called from C code
  real_f(*p);
}

【讨论】:

    【解决方案2】:

    我的 n3000.pdf 副本(来自 here),在第 7.5 节中有这样的说法——联动规范

    9。从 C++ 到定义的对象的链接 其他语言和定义的对象 在其他语言的 C++ 中是 实现定义和 语言依赖。只有在哪里 两种对象布局策略 语言实现类似 这样的联系就够了。

    由于 C 和 C++ 是不同的语言,这意味着你不能依赖普通编译器的这个“特性”。

    更强的是同一部分中的注释 5(强调我的):

    如果两个声明声明了函数 同名和 参数类型列表(8.3.5)为 相同命名空间的成员或 将具有相同名称的对象声明为 是同一个命名空间的成员,并且 声明给出了名称 不同的语言联系, 程序格式错误; 无诊断 如果出现声明,则需要 在不同的翻译单元中

    所以,我想说的是,您所做的并不能保证按照标准工作,并且编译器不需要为您给出的示例打印诊断,因为 声明在不同的翻译单元中。

    仅供参考,它在 Snow Leopard 上使用 gcc 和 g++ 4.2.1 版“对我有用”。

    【讨论】:

    • 完全正确的报价,但请注意,它也有一个好处:因为它无论如何都是由实现定义的,如果它有效,它就有效。
    • 是的,但也许它可以工作,因为它适用于特定版本的编译器?我尝试查看 gcc 文档,但找不到任何信息。据我了解,这个问题更像是一个“理论”问题,试图找出标准是否允许这样的声明。我怀疑 OP 是否想在真实代码中做这样的事情,因为很容易避免这样做。
    • 代码已经投入生产并且可以正常工作,但我希望保持代码标准符合未来的目标,因为新平台或编译器可能会成为目标。
    • 在这种情况下,我认为标准很明确,所以如果更改代码很容易,你应该这样做。
    • @Alok,是的。这是最好的。
    【解决方案3】:

    reference 是对象的替代名称。从技术上讲,C 中没有任何东西可以直接映射到 C++ 引用。

    引用的明显实现是一个(常量)指针,每次使用时都会取消引用。因此,根据您的编译器如何实现引用,您的代码可能会起作用。但这是不正确的。

    解决方案是编写一个接收真实对象或指向该对象的指针的 C 函数,并从中调用 C++ 函数。

    【讨论】:

    • 不保证,但不一定不正确。其他语言如何调用 C++ 函数(包括extern "C" C++ 函数)超出了 C++ 标准的范围。就 C 而言,该函数已正确声明和调用。这是一个实施问题,您是否可以、应该或必须不依赖它取决于项目要求。
    【解决方案4】:

    我认为您已经得到了一些很好的答案,所以请在您的问题上指出一些额外的内容。

    我的拙见是 extern 只是创建了一个 C 符号。

    所以即使我添加了一个类对象,你的示例仍然“似乎有效”(gcc/g++)——我不得不尝试相信它:

    class A {};
    
    extern "C" void f(int &i, A a) {
        i++;
    }
    

    【讨论】:

      【解决方案5】:

      这就是 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 不需要存储在内存中(也许它只是存在于寄存器中,也许它消失在指令中)。

      【讨论】:

      • 你的意思是int (&amp;p)[100] = v[i].b;;还有 s/,/;/ 和 v.size().
      • 如果你能给出更完整的引用就好了。 哪里 Stroustrup 说的? (网站或书名、版本和页码)另一方面,我们已经知道引用通常被实现为指针。这就是为什么问题中的代码可以像今天这样工作的原因。我看不出你试图用这个答案表达的意思,特别是关于具有 C 链接的函数的参数。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-13
      • 1970-01-01
      • 2011-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多