首页 > 解决方案 > 如何定义 C 结构:c-linkage 和 udt

问题描述

我用 C-ABI 接口用语言 X 编写了 dll。我想从我的 c++ 程序中使用这个 C-ABI。

我在 main.cpp 中写道:

extern "C" {
struct Foo {
  const char * const data;
  unsigned len;
};
struct Foo f(void);
}

int main()
{
}

并收到编译器警告(visual c++/15.7.5/windows 7/32bit):

(7): 警告 C4190: 'f' 指定了 C 链接,但返回与 C 不兼容的 UDT 'Foo'

(7): 注意:见 'Foo' 的声明

这里godbolt链接:https ://godbolt.org/g/ztx1kf

我阅读了 C++ 代码链接中的错误:警告 C4190:类型已指定 C 链接,但返回与 C 不兼容的 UDT,但在我的情况下,我的 POD 结构中根本没有“c++ 代码”。

我怎样才能说服编译器这不是C++ struct Foo,而是C struct Foo

我尝试将其移动到单独的头文件(.h),但这没有任何改变。

如果我const char * const dataconst char *警告消失代替,我也不明白,但我不想改变结构的定义。

标签: c++visual-c++

解决方案


x64 调用约定文档解释说,eax如果 UDT 足够小并且符合某些标准,则会返回它们:

要在 RAX 中按值返回用户定义的类型,它的长度必须为 1、2、4、8、16、32 或 64 位。它还必须没有用户定义的构造函数、析构函数或复制赋值运算符;没有私有或受保护的非静态数据成员;没有引用类型的非静态数据成员;没有基类;没有虚函数;并且没有不符合这些要求的数据成员。

虽然Foo是一个StandardLayout类型(因此我们希望它可以工作),但const非静态数据成员使复制赋值运算符被删除,这可能就是他们的意思,即使它说“用户定义”。顺便说一句,这使它成为非TrivialType,因此不是 C++03 意义上的 POD。

它也超过了最大大小,但即使我们删除len了 ,上面仍然会阻止它成为“POD”。

同样,x86 调用约定文档解释了类似的内容:

[...] 8 字节结构除外,它们在 EDX:EAX 寄存器对中返回。较大的结构在 EAX 寄存器中作为指向隐藏返回结构的指针返回。[...] 不是 POD 的结构不会在寄存器中返回。

例如,如下所示的函数:

struct Foo
{ 
    const uint32_t x;
};

Foo f(void)
{
    Foo foo = { 12345 };
    return foo;
}

在 x86/x64 C++ 模式下编译时,Foo被认为是非 POD,因此eax/rax包含文档引导我们期望的对象的地址。

但是,当在 x86/x64 C 模式下编译时,Foo被认为是 POD(我们正在编译 C),因此您将uint32_t直接在eax.

f因此,即使我们将语言链接设置为 C,从 C调用也不起作用,这就是出现警告的原因。


推荐阅读