何时访问指向"死"对象的指针有效?

Oli*_*rth 53 c pointers undefined-behavior language-lawyer

首先,澄清一点,我不是在谈论解除引用无效指针!

考虑以下两个例子.

例1

typedef struct { int *p; } T;

T a = { malloc(sizeof(int) };
free(a.p);  // a.p is now indeterminate?
T b = a;    // Access through a non-character type?
Run Code Online (Sandbox Code Playgroud)

例2

void foo(int *p) {}

int *p = malloc(sizeof(int));
free(p);   // p is now indeterminate?
foo(p);    // Access through a non-character type?
Run Code Online (Sandbox Code Playgroud)

以上示例中的任何一个都会调用未定义的行为吗?

上下文

提出这个问题是为了回应这一讨论.建议是,例如,指针参数可以通过x86段寄存器传递给函数,这可能导致硬件异常.

根据C99标准,我们学习以下内容(强调我的):

[3.17] 不确定值 - 未指定的值或陷阱表示

然后:

[6.2.4 p2]当指向的对象到达其生命周期的末尾时,指针的值变得不确定.

然后:

[6.2.6.1 p5]某些对象表示不需要表示对象类型的值.如果对象的存储值具有这样的表示并且由不具有字符类型的左值表达式读取,则行为是未定义的.如果这样的表示是由副作用产生的,该副作用通过不具有字符类型的左值表达式修改对象的全部或任何部分,则行为是未定义的.这种表示称为陷阱表示.

综合所有这些,我们对访问"死"对象的指针有什么限制?

附录

虽然我引用了上面的C99标准,但我很想知道任何C++标准中的行为是否不同.

小智 30

示例2无效.你问题中的分析是正确的.

示例1有效.结构类型永远不会保留陷阱表示,即使其成员之一也存在.这意味着在陷阱表示会导致问题的系统上进行结构分配必须实现为逐字节复制,而不是逐个成员复制.

6.2.6类型的表示

6.2.6.1总则

6 [...]结构或联合对象的值永远不会表示,即使结构或联合对象的成员的值可能是陷阱表示.


R..*_*R.. 15

我的解释是,虽然只有非字符类型可以有陷阱表示,但任何类型都可以具有不确定的值,并且以任何方式访问具有不确定值的对象会调用未定义的行为.最臭名昭着的例子可能是OpenSSL无效使用未初始化的对象作为随机种子.

所以,你的问题的答案是:永远不会.

顺便说一句,一个有趣的结果不仅是指向对象,而且指针本身在此之后是不确定的,free或者realloc是这个习惯用来调用未定义的行为:

void *tmp = realloc(ptr, newsize);
if (tmp != ptr) {
    /* ... */
}
Run Code Online (Sandbox Code Playgroud)

  • @OliCharlesworth,我认为那部分说:_如果一个对象的存储值具有这样的表示,并且由左值表达式**读取**,则表明它可以写入,但不能读取. (4认同)
  • @jimmcnamara:当然.但是在成功的情况下是UB,这就是重点. (4认同)
  • @OliCharlesworth,当然是.否则你怎么能这样做:`free(x); x = NULL;`? (3认同)