【发布时间】:2021-07-10 14:40:04
【问题描述】:
这个问题是我之前提出的问题的延伸。但是,一段时间后,我发现我对Conversion Behavior between Two Pointers 的一些概念仍然模棱两可。
为了便于讨论,我首先对主机实现做如下假设:
- malloc:8 对齐
-
sizeof(int):4,_Alignof(int):4 -
sizeof(double):8,_Alignof(double):8
问题一:
void *ptr = malloc(4096); // (A)
*(int *) ptr = 10; // (B)
/*
* Does the following line have undefined behavior
* or violate strict aliasing rules?
*/
*(((double *) ptr) + 2) = 1.618; // (C)
// now, can still read integer value with (*(int *) ptr)
以我目前的理解,答案是否。
根据 C11 的 [6.3.2.3 #7]:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未正确对齐引用类型,则行为未定义。 ...
和 C11 的 [6.5 #7]:
对象的存储值只能由具有以下类型之一的左值表达式访问:
- 与对象的有效类型兼容的类型,
- ...
因此,据我所知,
- 在 (A) 行之后,我分配了一个没有声明类型且还没有有效类型的对象。
- 在 (B) 行之后,分配对象的前 4 个字节已经具有有效类型:
int。 - 对于 (C) 行,
ptr与double类型正确对齐,指针转换和指针算术是合法的。因为它没有访问前 4 个字节,所以它没有违反 6.5 #7 规则。
我对上面提到的内容有什么误解吗?
问题二:
void *ptr = malloc(4096); // (A)
*(int *) ptr = 10; // (B)
/*
* Does the following line have undefined behavior
* or violate strict aliasing rules?
*/
*(double *) ptr = 1.618; // (C)
// now, shall not read value with (*(int *) ptr)
以我目前的理解,答案也是否。
根据C11的[6.5 #6]:
如果通过具有非字符类型类型的左值将值存储到没有声明类型的对象中,则左值的类型将成为该访问和后续对象的有效类型不修改存储值的访问。
所以,据我所知,行 (C) 是一个后续访问,它修改了存储的值并将前 8 个字节的有效类型更新为double。我对上面提到的内容有什么误解吗?
主要的困惑是不确定是否违反了[6.5 #7]规则:
对象的存储值只能由具有以下类型之一的左值表达式访问:
- 与对象的有效类型兼容的类型,
- ...
【问题讨论】:
-
有效类型规则被严重破坏了。因为它确实说“如果通过具有非字符类型类型的左值将值存储到没有声明类型的对象中,则左值的类型将成为该访问和后续访问的对象的有效类型不修改存储值。”但是你在这里做了一个写访问,所以:“对于没有声明类型的对象的所有其他访问,对象的有效类型就是用于访问的左值的类型。”这意味着您也可以通过 double 访问原始对象。
-
我不认为他们打算让标准按字面意思阅读,但这部分语言只是被破坏了。
-
@Lundin:回复“意味着您也可以通过 double 访问原始对象”:“所有其他访问”部分不适用于此处。当他们在做
* (double *) ptr =时,它被“如果一个值通过一个非字符类型的左值存储到一个没有声明类型的对象中”覆盖,而不是“所有其他”子句。如果他们在写了int之后正在阅读它,它会被“……用于不修改存储值的后续访问”所涵盖,并且再次不属于“所有其他”子句。 -
@EricPostpischil 是的,我同意,我提到了 OP 的 Q2。
-
考虑[6.5 #6]和[6.5 #7]顺序应用可能会有所帮助,这样如果对象的有效类型被#6改变,那么它已经兼容#7中左值表达式的类型。
标签: c pointers language-lawyer strict-aliasing