如何定义C结构:c-linkage和udt

时间:2018-07-15 11:06:19

标签: c++ visual-c++

我有使用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”的声明

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

我读了Error in C++ code linkage: warning C4190: type has C-linkage specified, but returns UDT which is incompatible with C,但就我而言,我没有“ c ++代码” 完全在我的POD结构中。

如何使编译器相信它不是 C ++ struct Foo,而是 C struct Foo

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

如果我将const char * const data替换为const char *警告消失, 我还不了解的内容,但我不想更改struct的定义。

3 个答案:

答案 0 :(得分:2)

extern "C"不允许您在C ++程序中嵌入任意C代码。它做的唯一一件事就是抑制C ++特定的名称修饰和调用声明的约定,以便可以用其他方式调用用extern "C"(或等效地在extern "C"块中声明)的函数。语言。 (变量声明也可以是extern "C",尽管在正常使用中这无效。)类型定义不受此类限制的影响。

在您的情况下,问题是声明const char * const data,它是指向const char*的const指针。 C不支持const struct成员(如果没有成员初始值设定项列表,它实际上没有任何意义),因此Foo不是合法的C struct定义。删除第二个const,一切都会好起来。

没有办法更改结构定义,因为您的C编译器不会接受该结构。如果您需要保持与C ++结构的兼容性,那么唯一的选择就是定义两个结构,然后将数据复制过来。

答案 1 :(得分:0)

C, of course, can have const struct member

某种解决方案(截至今天):如果使用MSVC,只需禁用警告4190。

答案 2 :(得分:0)

x64 calling convention docs解释说,如果UDT足够小并符合某些条件,则它们将在eax中返回:

  

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

尽管Foo StandardLayout 类型(因此我们希望它可以工作),但const非静态数据成员会删除副本分配运算符,即使它说的是“用户定义”,这也可能是它们的意思。顺便说一下,这使其成为非 TrivialType ,因此不是C ++ 03意义上的POD。

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

类似地,x86 calling convention docs解释了类似的内容:

  

[...]除了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的值。 / p>

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