当前位置: 首页 > 知识库问答 >
问题:

指针到成员的C类内初始化使MSVC失败(但GCC/Clang工作)

赵雅懿
2023-03-14

下面是一个非常简单的C代码:

#include <iostream>

struct A
{
    int a;
    constexpr static int A::* p = &A::a;
    virtual void f() {}
};
int main()
{
    A x; x.a = 0;
    x.*(A::p) = 1234;
    std::cout << x.a;
}

更令人震惊的是,这段代码显示了GCC、Clang和MSVC之间不同的结果。

我试过4个编译器

>

  • GCC:编译良好,打印1234

    叮当声:编译良好,打印1234

    MSVC(在线):编译失败。

    带有Visual Studio 2019的MSVC(本地):编译良好,打印0。(有趣的是,如果我删除f(),它会打印1234。)

    我不确定指向成员的指针及其自己的成员的类内初始化是否合法,但我相信这段代码应该打印1234

    在编译器资源管理器或Rextester上试用。

    我不知道谁是对的。(至少4.似乎是一个bug)

    编辑-我发现3.和4.的差异来自编译器选项/permissive。但是/permissive和no-/permissive的结果仍然与gcc和clang不同。

  • 共有1个答案

    慕皓君
    2023-03-14

    更新

    这是一个稍微修改的代码,它似乎像你预期的那样工作,在Visual Studio 2019中:

    struct A
    {
        int a;
        constexpr static int A::* p() { return &A::a; }
        virtual void f() {}
    };
    
    using namespace std;
    int main()
    {
        A x;
        void* px = &x;
        x.a = 0;
        x.*(A::p()) = 1234;
        cout << x.a << endl;
    
        getchar();
    }
    

    更新结束

    刚刚在Visual Studio 2019上试用,我将代码修改为:

    #include <iostream>
    
    struct A
    {
        int a;
        constexpr static int A::* p = &A::a;
        virtual void f() {}
    };
    
    struct B
    {
        int a;
        constexpr static int B::* p = &B::a;
    };
    
    using namespace std;
    
    void printBytes(void* p, size_t length = 16) {
        uint8_t* pu = (uint8_t*)p;
        cout << "  ";
        for (size_t i = 0; i < length; ++i) {
            auto val = *(pu + i);
            printf("%02x ", val);
        }
        cout << endl;
    }
    
    int main()
    {
        A x;
        void* px = &x;
        cout << "\nfor A:\n";
        printBytes(px);
        x.a = 0;
        printBytes(px);
        x.*(A::p) = 0x1234;
        printBytes(px);
        cout << x.a << endl;
    
        B y;
        void* py = &y;
        cout << "\nfor B:\n";
        printBytes(py);
        y.a = 0;
        printBytes(py);
        y.*(B::p) = 0x1234;
        printBytes(py);
        cout << y.a << endl;
    
        getchar();
    }
    

    输出为:

    
    for A:
      fc 9c 2c 01 cc cc cc cc cc cc cc cc 0f 40 04 09
      fc 9c 2c 01 00 00 00 00 cc cc cc cc 0f 40 04 09
      34 12 00 00 00 00 00 00 cc cc cc cc 0f 40 04 09
    0
    
    for B:
      cc cc cc cc cc cc cc cc cc cc cc cc 60 fd 3d 01
      00 00 00 00 cc cc cc cc cc cc cc cc 60 fd 3d 01
      34 12 00 00 cc cc cc cc cc cc cc cc 60 fd 3d 01
    4660
    

    B 事例按预期工作,但对于 A 事例,上面的输出表明以下行:

    < code > x . *(A::p)= 0x 1234;

    将< code>0x1234写入“指向虚函数表的指针”所在的位置。

    所以,我认为Visual Studio 2019的编译器在表达式< code >上犯了一个错误

     类似资料:
    • 在C++98标准里,只有static const声明的整型成员能在类内部初始化,并且初始化值必须是常量表达式。这些限制确保了初始化操作可以在编译时期进行。例如: int var = 7; class X { static const int m1 = 7; // 正确 const int m2 = 7; // 错误:无static static int m3 =

    • 请查看以下代码: gcc和clang编译并链接它时没有任何问题,而MSVC(2015、2019)生成了未解决的外部符号“void _cdecl test(int*)” 请注意,如果从库中删除const关键字。cpp然后MSVC链接代码。 谁在这里?是MSVC的错误吗?

    • 问题内容: 我最近刚与Python中的一个错误作斗争。那是那些愚蠢的新手错误之一,但是它让我思考了Python的机制(我是C ++的老程序员,是Python的新手)。我将列出错误的代码并解释如何解决该问题,然后我有两个问题。 场景:我有一个叫做A的类,它有一个字典数据成员,下面是其代码(当然这是简化的): 使用此代码的类为B类: 请注意,每次调用都会初始化类A的新“干净”实例,并在添加前后打印字典

    • 我正在尝试同时处理MSVC和GCC编译器,同时更新这个代码库以在GCC上工作。但我不确定GCCs内联ASM是如何工作的。现在我并不擅长将ASM转换为C,否则我就会使用C而不是ASM。 我假设ROR13的工作方式类似于,但代码不会产生相同的输出。 什么是将这个内联ASM翻译成GCC的正确方法,或者这个代码的C翻译是什么?

    • 这个问题的后续问题 我们有以下代码: 这种访问是一种未定义的行为吗?一方面,不需要对象来访问静态类成员,另一方面,

    • 对于中继中的工作正常,但对于来自中继的,失败并显示错误消息(链接): 错误:未为此 lambda 函数捕获“此” 在命名空间范围内定义对两者都适用。 使用 lambda 捕获稍微修改的定义也适用于全局或本地范围。 哪个编译器是对的?