指向数组第一个元素之前的指针

Pointer to one before first element of array

在 C 语言中,当指针引用同一个数组或数组末尾后的一个元素时,算术和比较是明确定义的。那么数组第一个元素之前的一个呢?只要我不取消引用就可以吗?

给定

int a[10], *p;
p = a;

(1)写--p合法吗?

(2) 表达式中写p-1合法吗?

(3) 如果 (2) 没问题,我可以断言 p-1 < a 吗?

这有一些实际问题。考虑一个 reverse() 函数,它反转以 '[=16=]'.

结尾的 C 字符串
#include <stdio.h>

void reverse(char *p)
{
    char *b, t;

    b = p;
    while (*p != '[=11=]')
        p++;
    if (p == b)      /* Do I really need */
        return;      /* these two lines? */
    for (p--; b < p; b++, p--)
        t = *b, *b = *p, *p = t;
}

int main(void)
{
    char a[] = "Hello";

    reverse(a);
    printf("%s\n", a);
    return 0;
}

我真的需要在代码中进行检查吗?

请从language-lawyer/practical的角度分享您的想法,以及您将如何应对这种情况。

(1) Is it legal to write --p?

它是 C 语法允许的 "legal",但它会调用未定义的行为。为了在标准中找到相关部分,--p 等同于 p = p - 1(除了 p 只计算一次)。那么:

C17 6.5.6/8

If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

评估调用未定义的行为,这意味着您是否取消引用指针并不重要 - 您已经调用了未定义的行为。

此外:

C17 6.5.6/9:

When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object;

如果您的代码违反了 ISO 标准中的 "shall",它会调用未定义的行为。

(2) Is it legal to write p-1 in an expression?

与 (1) 相同,未定义的行为。


至于这在实践中如何导致问题的示例:假设数组位于有效内存页的最开头。当您在该页面之外递减时,可能会出现硬件异常或指针陷阱表示。对于微控制器来说,这并非完全不可能的情况,尤其是当它们使用分段内存映射时。

使用这种指针算法是一种糟糕的编码习惯,因为它可能会导致大量难以调试的问题。

这种东西我20多年才用过一次。我正在编写一个回调函数,但我无法访问正确的数据。调用函数在 内部 一个适当的数组中提供了一个指针,我需要该指针之前的字节。

考虑到我接触到了整个源代码,并且我多次验证了行为以证明我得到了我需要的东西,并且我已经让其他同事审查过,所以我决定放弃它投入生产。

正确的解决方案是将调用函数更改为 return 正确的指针,但考虑到时间和金钱,这是不可行的(软件的那部分是从第三方获得许可的) .

因此,a[-1] 是可能的,但只能在非常特殊的情况下非常小心地使用。否则,没有充分的理由去做那种自我伤害的巫术。


注意:仔细分析,在我的示例中,很明显我没有访问适当数组开始之前的元素,而是指针之前的元素,保证在同一个内部数组。


参考提供的代码:

  • 不能将 p[-1] 与 reverse(a); 一起使用;
  • 可以(-ish)将它与 reverse(a+1); 一起使用,因为您仍在数组中。