【问题标题】:Pointer to one before first element of array指向数组第一个元素之前的指针
【发布时间】:2020-02-11 07:13:12
【问题描述】:

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

给定

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

(1) 写--p合法吗?

(2) 在表达式中写p-1是否合法?

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

对此存在一些实际问题。考虑一个 reverse() 函数,它反转以 '\0' 结尾的 C 字符串。

#include <stdio.h>

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

    b = p;
    while (*p != '\0')
        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;
}

我真的需要检查代码吗?

请从语言律师/实践的角度分享您的想法,以及您将如何应对这种情况。

【问题讨论】:

  • (1) 写它可能是合法的,但如果你执行它,结果是未定义的行为。使用分段架构(英特尔 80286、80386 等),结果可能完全令人困惑。 (2) 同上。 (3) N/A,但答案是否定的。对于您的硬件和操作系统,您可能很安全,但 C 标准并不能保证这一点。
  • 这能回答你的问题吗? What are all the common undefined behaviours that a C++ programmer should know about? 专门查看指针部分。

标签: c language-lawyer pointer-arithmetic


【解决方案1】:

(1) 写--p是否合法?

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

C17 6.5.6/8

如果两个指针 操作数和结果指向同一数组对象的元素,或最后一个元素 数组对象的元素,评估不应产生溢出;否则,行为未定义。

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

此外:

C17 6.5.6/9:

当两个指针相减时,都指向同一个数组对象的元素,或者指向数组对象最后一个元素的元素;

如果您的代码违反 ISO 标准中的“应”,它会调用未定义的行为。

(2) 在表达式中写 p-1 是否合法?

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


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

【讨论】:

  • 您能否为问题中的示例代码提供一些 cmets?检查是否必要/适当/充分?
  • 开头可能是*p == '\0'。此检查旨在防止 for 循环中出现 p--
  • @aafulei 是的,我意识到了。由于循环写得不好,您确实需要额外的检查。正确的解决方法是重写循环,例如:godbolt.org/z/R4TuwT
  • 感谢您的代码。这是一个聪明的。我建议您将其放在答案中,以供更多人查看。唯一的问题是,当字符串中有奇数个字符时(不包括'\0'),最后会有一个自交换(与自身交换)。但这没关系。在我打勾之前,请耐心等待我进行交叉验证。
  • 如果表达式中的p-1 无效,则p=p-1 将无效。而p--p=p-1。你会认为递减指针是无效的吗?
【解决方案2】:

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

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

考虑到我可以访问整个源代码,并且我多次验证了该行为以证明我得到了我需要的东西,并且我让其他同事对其进行了审查,我决定让它投入生产是可以的.

正确的解决方案是更改调用函数以返回正确的指针,但考虑到时间和金钱(该部分软件已从第三方获得许可),这是不可行的。

所以,a[-1] 是可能的,但只能在非常特殊的情况下非常小心地使用。否则,就没有充分的理由去做那种自残的巫毒教了。


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


参考提供的代码:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多