什么时候指针表达式 "based on" 是另一个指针?

When is a pointer expression "based on" another pointer?

在关于restrictSection 6.7.3.1 of the C language standard中,它说:

  1. Let D be a declaration of an ordinary identifier that provides a means of designating an object P as a restrict-qualified pointer to type T.

  2. ...

  3. In what follows, a pointer expression E is said to be based on object P if (at some sequence point in the execution of B prior to the evaluation of E) modifying P to point to a copy of the array object into which it formerly pointed would change the value of E.

我不明白这句话的意思 - 字面意思:

有人可以帮我解释一下那段文字吗?

(灵感来自 this answer

Who said P was pointing to a "copy of an array object"?

指针运算是根据指向数组元素的指针定义的(在 C 2018 6.5.6 8 和 9 中)。为此,单个对象被视为一个元素的数组。因此,无论何时我们有任何非空对象指针,在这个模型中,它都指向一个数组。

Why did P "formerly" point to anything? That is, who says we've changed its value?

你引用的文字是说“为了弄清楚 E 是否基于 P,让我们假设复制 P 指向的数组然后赋值到 P 指向副本中相应位置的指针。”所以你引用的文字是说我们正在更改 P 的值,然后我们将 E 的值与此更改和没有此更改进行比较。

Let's suppose E is a pointer of local scope. Why would modifying any pointer expression other than the E pointer itself "change the value of E"? It could change the value pointed to by E maybe. Right?

对象和值没有作用域。标识符有作用域。但是让我们考虑一个具有块作用域的标识符:

// P is a pointer into A.
// S is the size of A.
// A is the start of an array not contained in any other array.
void foo(char *P, size_t S, char *A)
{
    void *E = P+2;
}

为了便于说明,假设 P 的值为 0x1004,而 A 的值为 0x1000。 E 是基于 P 吗?好吧,鉴于以上,E 是 0x1006。假设我们在定义 E:

之前考虑这段代码
    char *N = malloc(S);
    memcpy(N, A, S);
    P = P - A + N;

假设malloc returns 0x2000。 E 的值是多少?它将是 0x2006。这与 0x1006 不同。因此 E 是基于 P.

另一方面,考虑一下:

void foo(char **P, size_t S, char **A)
{
    #if OnOrOff
        char *N = malloc(S);
        memcpy(N, A, S);
        P = P - A + N;
    #endif
    char **E = P[3];
}

现在,E 的值会根据 OnOrOff 是真还是假而改变吗?不,在任何一种情况下,它都会直接或从副本中接收作为 A 的引用元素的值。 P 可能指向 AN 的事实不会影响 E 的值。所以这个E不是基于P.

"based on" 的定义旨在定义指针之间的传递关系,但它的实际措辞会产生一个不可行的定义,据我所知,它不符合任何实际的编译器行为。

传递以下规则会更简单(编译器似乎就是这样做的):如果 *p 是类型 T* 的指针,则以下指针是 "based on" p:

  • p+(intExpr)p-(intExpr)
  • (otherType*)p
  • &*p
  • &p->structMemberofNonArrayType&p->unionMemberofNonArrayType
  • p->structMemberofArrayTypep->unionMemberofArrayType
  • &p[intExpr]
  • 基于上述任何一项的任何指针

我认为标准对 (someType*)someIntegerFunction((uintptr_t)p) 并不清楚,我认为编译器编写者也不清楚。

请注意,通过上述任何表达式从 p 派生的任何 q,除了涉及通过 uintptr_t 进行强制转换的表达式外,(char*)p 和 [=28] 之间的区别=] 将独立于 p.

持有的地址

顺便说一句,这是一个有问题的极端情况的例子:

int test1(int * restrict p1, int * restrict p2, int n)
{
    int *restrict p3 = p1+n;
    // How would p4 and p5 be affected if p3 were replaced
    // with a pointer to a copy here?
    int *p4 = p3;
    if (p3 != p1) p4=p1;
    int *p5 = p2 + (p3 == p1);
    *p3 = 1;
    *p5 = 2;
    return *p4;
}

使用基于另一个指针的传递方式,如果 n 为零,p4 显然基于 p3。但是,指针 p5 不会从 p3 派生,因为没有 "based upon" 步序列可以派生其值。

尝试将标准中给出的规则应用于 n==0 情况,将 p3 替换为指向数组副本的指针不会影响 p4 的值,但会影响 p5 的值。这意味着 p4 不是基于 p3,但 p5 是以某种方式。

我认为这样的结果是荒谬的,我认为标准的作者也是如此,但它遵循标准中给出的规则,如措辞。

在阅读了几条评论以及@EricPostpischil 的回答后,我尝试综合我认为更清晰但更长一些的措辞来澄清事情并回答提出的问题。

原文:

In what follows, a pointer expression E is said to be based on object P if (at some sequence point in the execution of B prior to the evaluation of E) modifying P to point to a copy of the array object into which it formerly pointed would change the value of E.

澄清文本:

In what follows, a pointer expression E is said to be based on object P if changing P (with certain restrictions) before E is evaluated would cause E to evaluate to a different value. The restrictions are:

  • Trivial sanity restriction: The modification of P must occur at a sequence point.
  • P can only be modified to point to an identical copy of what it was pointing to originally.
    (And since in general, we can think of pointers always pointing to an array object - P can only be set to point at a copy of that array object).

第 3 点可以用代码表示(大致):

#define E(P)  ( (P) + 1 )   // put the expression you want to test here

extern T obj;    // T is some type
T copy = obj;

if ( E(&obj) != E(&copy) )
    printf("the expression in the macro is based on P")

标准中使用的正式语言定义允许 E 是非确定性和其他病态情况(例如 (P) + rand() % 5),而我的示例则不允许。

标准版本是我们将 E(&obj) 的结果与相同上下文中 E(&copy) 的结果进行比较。