来自MSVC外"C"的故事
Tales from the MSVC extern "C"
[这个问题有一个我能找到的重复项,但这个答案是完全错误的,请参阅下面的 C 代码。
我知道extern "C"
不会在您的C++中间生成 C 代码。它只是一个链接指令。
我有几个这样的extern "C"
故事要讲,但这里有一个今天困扰我的故事。这是一个完全最新的VS2019,这是代码:
#include <stdint.h>
#include <stdlib.h>
#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 {
/* remove this const and MSVC makes no warning
leave it in and MSVC complains, a lot
GCC or clang could not care less
*/
const
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( void )
{
constexpr auto test_{ make_Test(42) };
return test_.x ;
}
链接到强制性的 GODBOLT: https://godbolt.org/z/ecdz1vqhq
关于这个常量的评论是我问题的要点。
MSVC extern "C" 在很大程度上(完全?(没有记录。因此,我无法判断我是否在这个无证区域违反了一些规则。许多人声称这是某种"未完全实施"的C11。
对于 C11(或任何其他 C(结构成员类型具有该常量是完全可以的。当然,老海湾合作委员会也不在乎。正如可见的我那 GODBOLT 上线。
这只是VS2019中的一个错误,还是我制造了一个错误?
更新
即使我将make_Test
的实现移动到单独的 C 文件中,并将其显式编译为 C,此警告也将保持不变。
关于之前同一问题的"答案"。C 可以有 const 结构数据成员,当然,C 结构可以在制作时进行列表初始化。请参阅下面的代码:
// 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
形式返回:
要在 RAX 中按值返回用户定义类型,其长度必须为 1、2、4、8、16、32 或 64 位。它还必须没有用户定义的构造函数、析构函数或复制赋值运算符;没有私有或受保护的非静态数据成员;没有引用类型的非静态数据成员;没有基类;没有虚拟功能;并且没有不满足这些要求的数据成员。
虽然Test
是StandardLayout类型(因此我们希望它有效(,但const
非静态数据成员会删除复制赋值运算符,这可能就是它们的意思,即使它说"用户定义"。顺便说一下,这使得它成为一个非平凡类型,因此不是 C++03 意义上的 POD。
同样,x86 调用约定文档也解释了类似内容:
不是 POD 的结构将不会在寄存器中返回。
例如,如下所示的函数:
Test f(void)
{
Test test = { 12345 };
return test;
}
在 x86/x64 C++模式下编译时,Test
被视为非 POD,因此eax
/rax
包含文档引导我们期望的对象地址。
但是,当编译器在 x86/x64 C 模式下时,Test
被视为 POD(我们正在编译 C(,因此您将直接在eax
中获取uint32_t
值。
因此,即使我们将语言链接设置为 C,从 C 调用f
也不起作用,这就是出现警告的原因。
C++标准中没有要求C++实现与 C 实现配对
。C++标准中没有要求在 C 和 C++ 中具有相似含义的所有构造都应该在 C++ 实现与所有或部分 C 实现之间二进制兼容。甚至任何地方都没有要求同一平台上的两个 C 实现应该兼容。
举一个非常简单的例子,可以有一个带有sizeof(long) == 4
的实现(C 或 C++(,而在同一平台上(C 或 C++(上有一个sizeof(long) == 8
实现,这绝对没有错。
回到问题中的特定结构,任何地方都没有要求任何特定的struct
,虽然在 C 和 C++ 中都是完全合法的,但在特定的 C 实现和特定的 C++ 实现中具有相同的布局或相同的参数传递约定。
extern "C"
帮助程序员生成与 C 语言互操作的代码,但该标准不能保证任何特定的构造都可以工作,因为 C++ 标准不控制 C 实现。
TL;DR 这不是不符合任何标准的情况,这是特定C++实现和特定 C 实现之间的不幸但完全合法的不兼容。由于它们都来自同一个生产者,因此他们知道不兼容,并且您会得到一个很好的警告。
- 为什么使用__LINE_的代码在发布模式下在MSVC下编译,而不是在调试模式下
- MSVC是否支持C++11样式的属性而不是__declspec
- C++17中函数模板中的静态数组初始化(MSVC 2019)
- 有与__builtin__FUNCTION()等效的MSVC吗
- 正在解码MSVC 32位版本的程序集(作业).没有手术做什么
- MSVC 忽略 [[maybe_unused]] 的功能?
- std::vector::p ush_back() 不会在 MSVC 上编译具有已删除移动构造函数的对象
- MSVC中的宏观扩展问题
- 不正确的操作数类型 MSVC
- CRTP 中的复制赋值运算符 - gcc vs clang 和 msvc
- MSVC(Visual C++)是否有等效的-weffc ++?
- 命名空间范围内的外部 - GCC vs clang vs msvc
- Visual C++: MSVC vs. GCC+CLANG: 处理 lambda 捕获类成员变量,正确的方法是什么?
- CMake 生成的 MSVC 项目找不到符号,即使为其相关 dll 正确生成了 lib 文件也是如此
- 防止临时对象文件访问 MSVC 中的磁盘
- 来自MSVC外"C"的故事
- 派生到矩阵库转换背后的故事<Derived>
- 缩小从"int"(常量表达式)到"无符号int"的转换 - MSVC vs gc
- 一个关于字符串流、十六进制和字符的故事
- 同样的故事——VS VS GCC 4.6.1