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

首先,为了澄清,我不是在引用无效指针的引用!

考虑以下两个例子。

例1

typedef struct { int *p; } T; T a = { malloc(sizeof(int) }; free(ap); // ap is now indeterminate? T b = a; // Access through a non-character type? 

例2

 void foo(int *p) {} int *p = malloc(sizeof(int)); free(p); // p is now indeterminate? foo(p); // Access through a non-character type? 

上面的例子中的任何一个调用未定义的行为?

上下文

这个问题是针对这个讨论而提出的 。 build议是,例如,指针参数可能会通过x86段寄存器传递给函数,这可能会导致硬件exception。

从C99标准,我们学习以下(强调我的):

不确定的值 – 一个未指定的值或一个陷阱表示

接着:

当指向的对象达到其生命周期结束时,指针的值变得不确定

接着:

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

综合所有这些,我们在访问指向“死”对象的指针方面有什么限制?

附录

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

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

例1有效。 一个结构types从不拥有一个陷阱表示,即使它的成员之一。 这意味着在陷阱表示会导致问题的系统上的结构分配必须作为一个字节拷贝来实现,而不是逐个成员的拷贝。

6.2.6types的表示

6.2.6.1总则

6 […]结构或联合对象的值绝不是说唱表示,即使结构或联合对象的成员的值可能是陷阱表示。

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

所以,你的问题的答案是:从来没有。

顺便说一下,不仅指向对象而且指针本身free或重新分配之后不确定的一个有趣的结果是,这个习惯用法调用未定义的行为:

 void *tmp = realloc(ptr, newsize); if (tmp != ptr) { /* ... */ } 

C ++的讨论

简而言之:在C ++中,不存在“读取”类实例的情况; 你只能“读”非类对象,这是通过左值到右值转换完成的。

详细答案:

 typedef struct { int *p; } T; 

T指定一个未命名的类。 为了讨论,我们来命名这个类T

 struct T { int *p; }; 

因为你没有声明一个拷贝构造函数,所以编译器隐式地声明了一个拷贝构造函数,所以类的定义如下:

 struct T { int *p; T (const T&); }; 

所以我们有:

 T a; T b = a; // Access through a non-character type? 

确实是的; 这是通过拷贝构造函数初始化的,所以拷贝构造函数的定义将由编译器生成; 定义等同于

 inline T::T (const T& rhs) : p(rhs.p) { } 

所以你是作为一个指针访问的价值 ,而不是一堆字节。

如果指针值无效(未初始化,释放),则行为未定义。