关于 c : memcpy() 可以用来改变”const”成员数据吗?

can memcpy() be used to change "const" member data?

对于具有 const 成员的 struct

1
struct point { const int x; const int y; };

用作成员数据

1
2
3
4
5
struct Foo
{
    point pt{ 0, 0 };
    void move_x(int value);
};

如何写Foo::move_x()来更新Foo::pt?可以使用 memcpy() 吗?

1
2
3
4
5
6
#include <memory.h>
void Foo::move_x(int value)
{
    const point pt_{ pt.x + value, pt.y };
    (void) memcpy(&pt, &pt_, sizeof(pt_)); // pt = pt_;
}

这可以使用指针安全地完成

1
2
3
4
5
6
7
8
9
10
11
12
#include <memory>
struct Bar
{
    std::unique_ptr<point> pPt_{ new point{ 0, 0 } };
    const point& pt() const {
        return *pPt_;
    }

    void move_x(int value) {
        pPt_.reset(new point{ pt().x + value, pt().y });
    }
};

但是 point 总是存储在堆中而不是 Bar 中。

请注意,客户根本不关心 point

1
2
3
4
5
   Foo foo;
   foo.move_x(314); // (314, 0)

   Bar bar;
   bar.move_x(3141); // (3141, 0)


这显然是未定义的行为。现在,"可以"这样做吗?当然,它可以,但只是在编译器不会抱怨的意义上。但是 C 的一方面是因为编译器不抱怨它并不意味着生成的代码可以正常工作。

有人编写一些读取 pt、调用 move_x()、然后再次读取 pt 的代码只是时间问题。

一个现代的优化编译器会正确地假设,因为代码正在读取 const 实例,它们的值不能改变,然后继续并优化从 pt 的第二次读取,使用从第一次读取缓存的数据pt,在 CPU 寄存器中。

然后,倒霉的程序员会花一周时间试图弄清楚为什么代码显然没有按照程序所说的那样做。


尝试将 memcpyconst 成员一起使用时会出现编译器错误,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <cstdlib>
#include <cstdio>
#include <cstring>

struct foo
{
    public:
        foo(int init_x);
        const int x;
        int bar();
};

foo::foo(int init_x): x(init_x) {}

int foo::bar()
{
    int *y = (int *) malloc(sizeof(int));
    *y = 6;
    memcpy(&x, y, sizeof(int));
    return x;
}

int main(int argc, char *argv[])
{
    foo f{5};
    printf("%d\
"
, f.bar());
}

以上结果

1
2
error: invalid conversion from a€?const void*a€? to a€?void*a€? [-fpermissive]
    memcpy(&x, y, sizeof(int));

虽然我在此示例中使用了 const int,但如果您改为使用 const int 指针成员,即

,您会发现相同的结果

1
const int *x;

但是,如果您删除 const 描述符并使用:

1
int x;

(或 int *x;,就此而言)错误不再发生,程序打印 6,正如人们所期望的那样。

所以这引出了一个问题,如果你知道某些东西将被声明为 const:

  • 如果您有能力更改声明,您不能自己删除 const 吗?
  • 如果您无法更改声明,那么您是否有充分的理由试图打破 const 做出的"Promise"?