如果两个指针指向同一个 struct 变量,是否定义了将一个取消引用的指针分配给另一个指针的行为?

struct Struct {
    int member;
};

int main() {
    struct Struct s, *p1, *p2;
    s.member = 1234;
    p1 = p2 = &s;
    *p1 = *p2; // is this allowed?
}

我之所以这样问,主要是因为在this SO postthis SO post中,许多答案都指出 struct 赋值或多或少等同于memcpy,一些编译器甚至会为赋值语句生成memcpy调用.然而,当内存位置重叠时,memcpy没有定义,这就引出了一个问题:它是为赋值而定义的吗?

这似乎符合预期,但我想知道C标准是否对此有什么要说的.是否需要添加一张简单的支票

if (p1 != p2) *p1 = *p2;

还是用memmove


仅供参考,这是在我用C实现我自己的槽映射(如描述here)时出现的.当从容器中删除元素时,数组末尾的元素被复制到已删除的位置,以保持所有内容连续.如果被删除的元素在数组的末尾,它将被复制到它自己(不是特别重要,因为该元素被认为是被删除的,但无论如何让我感到好奇).

推荐答案

任务是明确的.对应的调用memcpy不会.

以下是标准中关于简单赋值的规定6.15.6.1

如果存储在对象中的值是从另一个对象读取的 它以任何方式与第一个对象的存储重叠,然后 重叠应准确,且两个对象应具有合格或 兼容类型的非限定版本;否则,行为为 未定义.

鉴于memcpy(7.24.2.1第2段):

如果在重叠的对象之间进行复制,则行为为 未定义.

因此, struct 赋值等于memcpy是不正确的,尽管"或多或少"的限定是合适的.首先,正如我们在这里看到的,完全重叠的对象之间的memcpy是未定义的,而赋值是定义良好的.另一方面,memcpy保留任何填充字节(成员之间或最后一个成员之后的间隔)的值,而Assignment不能保证这样做.通过复制每个成员而不是复制整个 struct 来实现分配could.

也就是说,实现可以以完全相同的方式实现赋值和memcpy.事实上,某些情况下有未定义的行为,这意味着实现可以自由地做任何方便的事情.

C++相关问答推荐

传递给空闲的无效地址0x71 db7 cb5e0:未分配值

为什么PLT表中没有push指令?

有效地计算由一组点构成的等边三角形和等腰三角形的数量

需要大整数和浮点数.使用long long int和long double

如何将不同长度的位转换成字节数组?

我编译了一个新的c程序,并收到以下错误

以前版本的tty_ldisc_ops.ioctl()是否也需要文件参数?

变量>;-1如何在C中准确求值?

在CLANG中调试预处理器宏

理解C版宏(看起来像未声明的变量?)

如何使解释器存储变量

防止规范模式在C++中 echo 特殊字符

Vcpkg的配置文件

C I/O:在Windows控制台上处理键盘输入

合并对 struct 数组进行排序

为什么我的旧式&q;函数在传递浮点数时会打印2?

赋值两侧的后置增量,字符指针

Tcl_GetDoubleFromObj在列表的迭代中是一个缺点

通过GTK';传递回调参数;s g_signal_connect()导致C中出现意外值

`void foo(int a[static 0]);` 有效吗?