Ric*_*ant 6 c pointers strict-aliasing language-lawyer
这个问题是我之前问过的问题的延伸。但是,经过一段时间后,我发现我的一些关于两个指针之间的转换行为的概念仍然很模糊。
为了便于讨论,我首先对主机实现做如下假设:
sizeof(int): 4, _Alignof(int): 4sizeof(double): 8, _Alignof(double): 8void *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)
Run Code Online (Sandbox Code Playgroud)
以我目前的理解,答案是否定的。
根据 C11 的 [6.3.2.3 #7]:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未针对引用类型正确对齐,则行为未定义。...
和 C11 的 [6.5 #7]:
对象的存储值只能由具有以下类型之一的左值表达式访问:
- 与对象的有效类型兼容的类型,
- ...
因此,据我所知,
int。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)
Run Code Online (Sandbox Code Playgroud)
以我目前的理解,答案也是No。
根据 C11 的 [6.5 #6]:
如果一个值通过一个类型不是字符类型的左值存储到一个没有声明类型的对象中,那么左值的类型将成为该访问的对象的有效类型,并且对于不修改储值。
因此,据我所知,第 (C) 行是修改存储值并将前 8 个字节的有效类型更新为的后续访问double。我对上面提到的内容有什么误解吗?
主要的困惑是不确定是否违反了 [6.5 #7] 规则:
对象的存储值只能由具有以下类型之一的左值表达式访问:
- 与对象的有效类型兼容的类型,
- ...
为了方便讨论,我首先对主机实现做出以下假设 [...]
这些假设几乎完全无关紧要。对于所提出的特定问题而言,唯一重要的限制是sizeof(int) <= 2 * sizeof(double)。
特别是,malloc()保证分配一个与任何内置类型适当对齐的块。
问题一:
您的分析是正确的:不存在严格别名违规。
问题二:
根据C11的[6.5#6]:
Run Code Online (Sandbox Code Playgroud)If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the左值的类型成为该访问以及不修改存储值的后续访问的对象的有效类型。
因此,据我所知,行(C)是后续访问,它修改存储的值并将前 8 个字节的有效类型更新为双倍。
是的,行 (C) 修改了 的存储值*(double *) ptr,并且尽管ptr具有声明的类型,但由 指定的对象*(double *) ptr(作为动态分配块的一部分)却没有。因此,到第 6.5/6 段,由 指定的对象的有效类型*(double *) ptr成为表达式*(double *) ptr(即double)的类型,包括该访问本身。段落末尾的例外旨在避免该例外与 (B) 处的访问效果之间发生冲突。
因此,(C) 处不存在严格别名违规。用于访问的左值为*(double *)ptr。它的类型是double,根据 6.5/6,这也是正在访问的对象的有效类型,尽管该对象或其任何部分可能具有任何其他有效类型。这满足了特区的第一个方案。