解引用一个超出边界50%的指针(数组的数组)

Dereferencing a 50% out of bound pointer (array of array)

本文关键字:数组 边界 指针 一个 引用      更新时间:2023-10-16

这是我的"我不理解C和c++中的指针"集合中的一个新问题。

如果我混合了两个相同值的指针的位(指向相同的内存地址),恰好有完全相同的位表示,当一个是可解引用的,一个是一个过去的结束,标准说应该发生什么?

#include <stdio.h>
#include <string.h>
#include <assert.h>
// required: a == b
// returns a copy of both a and b into dest 
// (half of the bytes of either pointers)
int *copy2to1 (int *a, int *b) {
    // check input: 
    // not only the pointers must be equal
    assert (a == b);
    // also the representation must match exactly
    int *dest;
    size_t s = sizeof(dest);
    assert(memcmp(&a, &b, s) == 0); 
    // copy a and b into dest:
    // on "exotic" architectures, size does't have to be dividable by 2
    size_t half = s/2; // = floor(s/2), 
    char *pa = (char*)&a, *pb = (char*)&b, *pd = (char*)&dest;
    // copy half of a into dest:
    memcpy (pd, pa, half);
    // copy half of b into dest:
    memcpy (pd+half, pb+half, s-half); // s-half = ceil(s/2)
    //printf ("a:%p b:%p dest:%p n", a, b, dest);    
    // check result
    assert(memcmp(&dest, &a, s) == 0);
    assert(memcmp(&dest, &b, s) == 0);
    return dest;
}
#define S 1 // size of inner array
int main(void) {
    int a[2][S] = {{1},{2}};
    int *past = a[0] + S, // one past the end of inner array a[0]
        *val = &a[1][0], // valid dereferenceable pointer
        *mix = copy2to1 (past, val);
    #define PRINT(x) printf ("%s=%p, *%s=%dn",#x,x,#x,*x)
    PRINT(past);
    PRINT(mix);
    PRINT(val);
    return 0;
}

我真正想理解的是:"p指向对象x"是什么意思?

参见

这个问题是我之前关于数组的数组的问题的一个更好的版本:

  • 是一个指针的内存相同的分配?这是我另一个问题的变体:
  • 解引用一个包含对象(数组的数组)地址的超出绑定的指针

和其他有关指针有效性的问题:

  • 指针变量只是整数与一些操作符或他们是"神秘的"?
  • 和c++问题:用相同类型的对象覆盖一个对象

In [basic.compound]:

如果类型为T的对象位于地址A,则使用类型为cv T*的指针,其值为地址A被认为指向该对象,,而不管该值是如何获得的

pastval有相同的地址,所以它们指向同一个对象。一个是第一行的"过了最后一个",第二个是第二行的第一个元素,这并不重要。在那个地址有一个有效的对象,所以这里的一切都是完全合理的。


在c++ 17中,从P0137开始,这改变了很多。现在,[basic.compound]将指针定义为:

指针类型的每个值都是下列值之一:
-指向对象或函数的指针(据说该指针指向对象或函数),或
- 指针超过对象的末尾(5.7),或者
-该类型的空指针值(4.11),或
- 无效的指针值

所以现在,past是第二种类型的值(指针经过结束),但val是第一种类型的值(指针指向)。这些是不同类别的值,不可比较:

指针类型的值是指向或超过对象结束的指针,分别表示该对象占用的内存(1.7)的第一个字节的地址,或该对象占用的存储结束后的第一个字节的地址。[注:指针经过对象的末尾(5.7)不被认为指向可能位于该地址的与该对象类型无关的对象。当指针所指向的存储达到其存储期限时,指针值将失效;见3.7。

past没有指向任何东西,因此将其内容视为与val相同不再有意义。

我真正想理解的是:"p指向对象x"是什么意思。

对象p包含一个值,该值对应对象x在内存中的位置。

就是这样。这就是它的全部含义。你似乎执意要把事情弄得更复杂。

指针类型不是算术类型,不能像那样任意修改。通过在左值上使用一元&操作符、使用不是一元sizeof或一元&操作符的操作数的数组表达式,或调用返回指针值的标准库函数,可以获得有效的指针值。

除此之外的一切(大小、表示、物理vs虚拟等)都是实现细节,当涉及到表示地址时,实现差异很大。这就是为什么标准没有说任何关于当你用指针值扮演弗兰肯斯坦博士时会发生什么。

如果您非常熟悉您的平台的寻址约定(包括虚拟的和物理的),并且您知道您的实现如何在内存中布局项以及它如何表示指针类型,并且您有一个有效的用例来以这种方式破解指针值,那么您可以随心而为—两种语言标准都没有在这个主题上说什么。