c - 指针的 memcpy 与赋值相同吗?

标签 c pointers language-lawyer memcpy

简介:这个问题是我收集的 C 和 C++(以及 C/C++ 公共(public)子集)问题的一部分,这些问题涉及允许具有严格相同字节表示的指针对象具有不同的“值”,即对某些操作采取不同的行为(包括在一个对象上具有已定义的行为,而在另一个对象上具有未定义的行为)。

已关注 another question which caused much confusion ,这是关于指针语义的问题,希望能够澄清问题:

该计划在所有情况下都有效吗?唯一有趣的部分是“pa1 == pb”分支。

#include <stdio.h>
#include <string.h>

int main() {
    int a[1] = { 0 }, *pa1 = &a[0] + 1, b = 1, *pb = &b;
    if (memcmp (&pa1, &pb, sizeof pa1) == 0) {
        int *p;
        printf ("pa1 == pb\n"); // interesting part
        memcpy (&p, &pa1, sizeof p); // make a copy of the representation
        memcpy (&pa1, &p, sizeof p); // pa1 is a copy of the bytes of pa1 now
        // and the bytes of pa1 happens to be the bytes of pb 
        *pa1 = 2; // does pa1 legally point to b?
    }
    else {
        printf ("pa1 != pb\n"); // failed experiment, nothing to see
        pa1 = &a[0]; // ensure well defined behavior in printf
    }
    printf ("b = %d *pa1 = %d\n", b, *pa1);
    return 0;
 }

我想要一个基于标准报价的答案。

编辑

应大众要求,我想了解的是:

  • 对于给定类型的指针,指针的语义“值”(根据规范的行为)仅由其数值(它包含的数字地址)决定?
  • 如果没有,是否可以仅复制指针中包含的物理地址,而忽略相关的语义?

这里假设某个超出结束指针的对象碰巧意外地指向了另一个对象;如何在结束指针之后使用这样的对象来访问另一个对象?

我有权做任何事情,除了使用另一个对象的地址的副本。 (这是一个理解 C 语言指针的游戏。)

IOW,我尝试像黑手党一样回收脏钱。但是我通过提取脏指针的值表示来回收它。然后它看起来就像干净的钱,我指的是指针。没有人能分辨出区别,不是吗?

最佳答案

问题是:

Is this program valid in all cases?

答案是“不,不是”。

<小时/>

该程序唯一有趣的部分是在 if 语句保护的 block 内发生的事情。保证控制表达式的真实性有些困难,因此我通过将变量移动到全局范围来对其进行了一些修改。同样的问题仍然存在:这个程序是否始终有效:

#include <stdio.h>
#include <string.h>

static int a[1] = { 2 };
static int b = 1;
static int *pa1 = &a[0] + 1;
static int *pb = &b;

int main(void) {
    if (memcmp (&pa1, &pb, sizeof pa1) == 0) {
        int *p;
        printf ("pa1 == pb\n"); // interesting part
        memcpy (&p, &pa1, sizeof p); // make a copy of the representation
        memcpy (&pa1, &p, sizeof p); // pa1 is a copy of the bytes of pa1 now
        // and the bytes of pa1 happens to be the bytes of pb 
        *pa1 = 2; // does pa1 legally point to b?
    }
}

现在,保护表达式在我的编译器上是正确的(当然,通过让它们具有静态存储持续时间,编译器无法真正证明它们在此期间没有被其他东西修改......)

指针pa1指向数组a末尾,并且是一个有效的指针,但不能取消引用,即*pa1 给定该值时具有未定义的行为。现在的情况是,将这个值复制到p并再次复制回来将使指针有效

答案是否定的,这仍然无效,但标准本身并没有非常明确地说明这一点。委员会回复C standard defect report DR 260说的是:

If two objects have identical bit-pattern representations and their types are the same they may still compare as unequal (for example if one object has an indeterminate value) and if one is an indeterminate value attempting to read such an object invokes undefined behavior. Implementations are permitted to track the origins of a bit-pattern and treat those representing an indeterminate value as distinct from those representing a determined value. They may also treat pointers based on different origins as distinct even though they are bitwise identical.

即你甚至不能得出这样的结论:如果 pa1pb 是相同类型的指针并且 memcmp (&pa1, &pb, sizeof pa1) == 0确实, pa1 == pb 也是必要的,更不用说将不可取消引用的指针 pa1 的位模式复制到另一个对象并再次返回会使 pa1 有效。

响应继续:

Note that using assignment or bitwise copying via memcpy or memmove of a determinate value makes the destination acquire the same determinate value.

即它确认 memcpy (&p, &pa1, sizeof p); 将导致 p 获取与 pa1 相同的值,它 以前没有

<小时/>

这不仅仅是一个理论问题 - 众所周知,编译器会跟踪指针的来源。例如the GCC manual指出

When casting from pointer to integer and back again, the resulting pointer must reference the same object as the original pointer, otherwise the behavior is undefined. That is, one may not use integer arithmetic to avoid the undefined behavior of pointer arithmetic as proscribed in C99 and C11 6.5.6/8.

即程序写成:

int a[1] = { 0 }, *pa1 = &a[0] + 1, b = 1, *pb = &b;
if (memcmp (&pa1, &pb, sizeof pa1) == 0) {
    uintptr_t tmp = (uintptr_t)&a[0]; // pointer to a[0]
    tmp += sizeof (a[0]); // value of address to a[1]
    pa1 = (int *)tmp;
    *pa1 = 2; // pa1 still would have the bit pattern of pb,
              // hold a valid pointer just past the end of array a,
              // but not legally point to pb
}

GCC 手册指出明显不合法

关于c - 指针的 memcpy 与赋值相同吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32048698/

相关文章:

c - 如何动态分配结构+指针

c++ - decltype( (A{}.int_member) ) 的正确结果是什么?

c - 从两个 128 位 block 中收集四个 32 位字

c++ - 使用 av_read_frame() 获取 2 个连续帧

c - C中简单赋值语句中的错误

c - 将字符串作为十六进制放入 Uint32_t

java - 如果我有 2 个引用类型,具有相同的值,是否意味着内存中只有 1 个对象

c++ - 为什么在 C++ 中,一个类的指针可以转换为另一个类的指针?

c++ - 在 C++ 中使用临时对象作为默认参数是否安全?

C++ 算法堆分配保证