来自 MSVC extern “C"的故事 [英] Tales from the MSVC extern "C"
问题描述
[这个问题有一个重复的我可以找到,但该答案完全错误,请参阅下面的 C 代码.]
[this question has one SO duplicate I can find, but that answer is plain wrong, see the C code below.]
我了解 extern "C"
不会在您的 C++ 中间生成 C 代码.它只是一个链接指令.
I understand extern "C"
does not produce C code in the middle of your C++. It is just a linkage directive.
我有一些 extern "C"
故事要讲,但今天有一个困扰我.这是一个完全最新的VS2019,代码如下:
I have a few of these extern "C"
tales to tell, but here is one that bothers me today. This is a completely up-to-date VS2019, and this is the code:
#ifdef __cplusplus
extern "C" {
#endif
// NOTE: in here it is still C++ code,
// extern "C" is a linkage directive
typedef struct Test Test;
struct Test {
const /* remove this const and MSVC makes no warning */
uint32_t x;
} ;
/*
MSVC throws warning C4190: 'make_Test' has C-linkage specified,
but returns UDT 'Test' which is incompatible with C
: see declaration of 'Test'
*/
inline constexpr Test make_Test(uint32_t x)
{
return Test{ x };
}
#ifdef __cplusplus
}
#endif
int main(const int argc, const char * argv[])
{
constexpr auto test_{ make_Test(42) };
return 42 ;
}
关于那个 const 的评论是我问题的要点.
That comment about that const is the gist of my question.
MSVC extern "C" 很大程度上(完全?)没有记录.因此,我无法判断我是否违反了这个无证区域的某些规则?许多人声称这是某种未完全实施"的 C11 吗?
MSVC extern "C" is largely (completely?) undocumented. Thus I am unable to tell if, I am breaking some rule in this undocumented zone? Many claim this is some kind of "not fully implemented" C11 in there?
AFAIK 具有 C11(或任何其他 C)结构成员类型的 const 非常好.好的老 G++ 不在乎:https://wandbox.org/permlink/7XfH2i21Yfnb7BDw当然.
AFAIK having that const for a C11 (or any other C) struct member type is quite OK. And good old G++ could not care less: https://wandbox.org/permlink/7XfH2i21Yfnb7BDw of course.
这只是 VS2019 中的一个错误,还是我犯了一个错误?
Is this just a bug in VS2019, or I made a bug?
更新
即使我将 make_Test
的实现移动到单独的 C 文件中,并将其显式编译为 C,此警告将保持不变.
Even if I move the implementation of make_Test
into separate C file, and explicitly compile it as C, this Warning will stay the same.
关于那个答案",来自 之前的同一个问题.C 可以有 const struct 数据成员,当然,C 结构可以在创建时进行列表初始化.看下面的代码:
About that 'answer' from the same question from before. C can have const struct data members, and of course, C structs can be list initialized when made. See the code below:
// gcc prog.c -Wall -Wextra -std=gnu11 "-Wno-unused-parameter" "-Wno-unused-variable"
#include <stdlib.h>
typedef struct Test { const long x; } Test;
static struct Test make_Test(long x)
{
struct Test test_ = { x } ;
return test_;
}
int main(const int argc, const char * argv[])
{
struct Test test_ = make_Test(42) ;
return 42;
}
推荐答案
x64 调用约定文档 解释了如果 UDT 足够小且符合某些条件,则在 eax
中返回:
The x64 calling convention docs explain that UDTs are returned in eax
if they are small enough and fit some criteria:
要在 RAX 中按值返回用户定义的类型,它的长度必须为 1、2、4、8、16、32 或 64 位.它还必须没有用户定义的构造函数、析构函数或复制赋值运算符;没有私有或受保护的非静态数据成员;没有引用类型的非静态数据成员;没有基类;没有虚函数;并且没有不满足这些要求的数据成员.
To return a user-defined type by value in RAX, it must have a length of 1, 2, 4, 8, 16, 32, or 64 bits. It must also have no user-defined constructor, destructor, or copy assignment operator; no private or protected non-static data members; no non-static data members of reference type; no base classes; no virtual functions; and no data members that do not also meet these requirements.
虽然 Test
是 StandardLayout 类型(因此我们希望它可以工作),const
非静态数据成员使得复制赋值运算符已删除,这可能就是它们的意思,即使它说用户定义".顺便说一下,这使它成为一个非 TrivialType,因此不是 C++03 意义上的 POD.
While Test
is a StandardLayout type (and as such we would expect it to work), the const
non-static data member makes the copy assignment operator deleted, which is probably what they mean, even if it says "user-defined". This makes it, by the way, a non TrivialType and therefore not a POD in the C++03 sense.
同样,x86 调用约定文档 解释了类似的内容:
Similarly, the x86 calling convention docs explain something similar:
非 POD 的结构不会在寄存器中返回.
Structures that are not PODs will not be returned in registers.
例如,像下面这样的函数:
For instance, a function like the following:
Test f(void)
{
Test test = { 12345 };
return test;
}
在 x86/x64 C++ 模式下编译时,Test
被认为是非 POD,因此 eax
/rax
包含文档引导我们期望的对象.
When compiled under x86/x64 C++ mode, Test
is considered a non-POD and therefore eax
/rax
contains the address of the object as the docs lead us to expect.
但是,在x86/x64 C模式下编译时,Test
被认为是一个POD(我们正在编译C),因此你会直接在uint32_t
中得到uint32_t
的值代码>eax.
However, when compiler under x86/x64 C mode, Test
is considered a POD (we are compiling C) and therefore you will get the uint32_t
value directly in eax
.
因此,即使我们将语言链接设置为 C,从 C 调用 f
也不起作用,这就是出现警告的原因.
Therefore, calling f
from C won't work, even if we set the language linkage to C, which is why the warning appears.
这篇关于来自 MSVC extern “C"的故事的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!