【问题标题】:Allowed operations on an possibly invalid pointer by the strict interpretation of the C Standard通过 C 标准的严格解释允许对可能无效的指针进行操作
【发布时间】:2015-07-09 16:28:07
【问题描述】:

原始问题

(请参阅“编辑:更新的场景”)

这个问题可能以一种或另一种方式与大量关于超出范围的对象的指针的未定义行为等问题重复。但我在这里找到的关于这个主题的所有问题大多是专门的用例。所以我想把这个问题颠倒过来,不是问是否禁止某事,而是问究竟什么是允许的?

有一个可能的场景:你有一个函数,它需要一个指针——你不知道它是否来自一个(仍然)有效的对象。哪些操作在所有情况下都不是未定义的行为?哪些可能有未指定的附带条件?

int * myFunc(const int * const A, int * B)
{
   ...
}

编辑:更新的场景

在问题的 cmets 和 Matt McNabbs answer 中指出,无论如何,UB 很可能已经上升,因为在场景中调用函数期间使用了无效的指针(s 值)。因此,我将稍微改变一下场景(按照Keith Thompsons answer 的示例):

int *ptr = malloc(sizeof *ptr);
/* the value of ptr is now valid, possibly NULL */
if (ptr != NULL) 
{
    /* the value of ptr is valid and non-null */
    free(ptr);
    /* the value of ptr is now invalid */

    ... /* here operations in question */
}

允许的操作列表:

(由您的答案和 cmets 完成和纠正。)

  • 定义明确:取指针变量的大小。例如。 sizeof(ptr)
  • 定义明确:取解引用指针的大小(假设它不是void *)。例如。 sizeof(*ptr)(参见 EOFJonathan Leffler 的 cmets)。
  • 定义明确:将另一个(有效)值分配给指针(而不是引用的变量!)。例如。 ptr = NULL;
  • 定义明确:访问指针的表示(来自Keith Thompsonanswer 的示例):

    unsigned char rep[sizeof ptr];
    memcpy(rep, &ptr, sizeof ptr); /* ok, accesses the representation */
                                   /* but not the value */
    

没有按照标准定义好的操作:

(由您的答案和 cmets 完成和纠正。)

这些操作通常被视为在无效指针上定义良好,但根据标准没有定义良好:

  • 未定义:比较指针的值(甚至与 NULL 指针常量)
  • 未定义:转换为整数值

与所有未定义的行为一样,您可能会在许多机器上(ab)使用指针而侥幸,但 C 标准不保证您会侥幸逃脱,并且存在(或曾经是)这样滥用指针会导致程序严重失败。

有关一般规则,请参阅Keith Thompson's answer — 以及下面的大量 cmets。

【问题讨论】:

  • 我,可能还有 Mark A,很想知道人们为什么投反对票。这对我来说似乎是一个很好的问题......
  • 没有投反对票,但这个问题的前提是错误的(来自 C Rationale):“不管如何创建无效指针,任何使用它都会产生未定义的行为。即使分配,与空指针常量比较,或与自身比较,在某些系统上可能会导致异常。"
  • @Jashaszun 这个问题太宽泛了。
  • 所以...你说的是没有允许的操作...这很苛刻。谢谢你提供的信息。
  • @Jashaszun 这个问题怎么可能同时变得广泛并导致“什么都不允许”? :-)

标签: c pointers undefined-behavior c89


【解决方案1】:

任何对无效指针的使用都有未定义的行为。

int *ptr = malloc(sizeof *ptr);
// the value of ptr is now valid, possibly NULL
if (ptr != NULL) {
    // the value of ptr is valid and non-null
    free(ptr);
    // the value of ptr is now invalid
    ptr; // UNDEFINED BEHAVIOR
}

引用:N15706.2.4p2:

当指针的值变得不确定时 它指向(或刚刚过去)的对象达到其生命周期的终点。

编译器很可能不会为表达式语句ptr; 生成任何代码;那当然是在未定义行为的范围内。

对指针 object 的任何不检索其值的操作都是(至少可能)明确定义的:

sizeof ptr;  // ok, doesn't use the value
sizeof *ptr; // ok, doesn't use the value, only the type
ptr = NULL;  // ok

你也可以在不访问其值的情况下访问指针对象的表示

unsigned char rep[sizeof ptr];
memcpy(rep, &ptr, sizeof ptr); // ok, accesses the representation
                               // but not the value

虽然您对结果无能为力。

【讨论】:

  • 即使代码可以访问指针的表示,也不能保证由此获得的字节可以用于任何有用的目的。鉴于char *p,*q; p=calloc(1,1); free(p); q=calloc(1,1);,我认为if (!memcmp(&p, &q, sizeof p)) (*p)++; 发射核导弹是合法的,因为p 是非法的,q 可以合法地具有相同的表示,以及观察pq 的行为具有相同的按位表示不会使p 有效。
【解决方案2】:

这个问题非常广泛。但要回答您的具体情况:

int * myFunc(const int * const A, int * B)

如果这个函数被一个无效的指针值调用,那么它已经导致了未定义的行为通过评估无效的指针值作为准备调用函数的一部分。

你所有“定义明确”的要点都没有明确定义,因为一旦发生 UB,猫就不能放回包里。

【讨论】:

  • 您能否详细说明在准备调用该函数时导致评估无效指针的原因?
  • 是的 - 我的情况对这个问题没有多大帮助 - 我明白了 - 你完全正确 - 如果我们得到一个已经无效的指针,那么肯定发生了 UB 但至少有一种情况我可以想到,其中指针(不受我们控制)在函数的入口点上是有效的,并且可能不在我们对其操作的点:函数可能被中断并且指针在“并行”中被释放" 任务。
  • 另外 - 除了误导性场景之外,似乎有一组非常有限的操作,它们适用并且总是会导致 UB。 - 我的问题在什么意义上很宽泛?
  • @GreenAsJade 是的,将其指定为函数参数
  • @MarkA。很多操作都会导致UB,而且很复杂。这取决于系统是否有硬件陷阱表示,以及从 C11 开始您是否曾经获取过变量的地址,并且在 C89/C99/C11 之间有所不同
猜你喜欢
  • 2011-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多