这段代码是否违反了严格的别名规则?
Does this code violate the strict aliasing rule?
问题:
下面这段代码是否违反了严格的别名规则?也就是说,是否允许智能编译器打印 00000
(或其他一些令人讨厌的效果),因为首先作为其他类型访问的缓冲区然后通过 int*
?
如果不是,将仅在大括号之前移动 ptr2
的定义和初始化(因此 ptr2
已经定义,当 ptr1
进入范围时) 打破它?
如果不是,删除大括号(所以 ptr1
和 ptr2
在同一范围内)会破坏它吗?
如果是,代码怎么修改?
奖励问题:如果代码没问题,并且 2. 或 3. 也不要破坏它,如何更改它以破坏严格的别名规则(例如,将大括号循环转换为使用 int16_t
)?
int i;
void *buf = calloc(5, sizeof(int)); // buf initialized to 0
{
char *ptr1 = buf;
for(i = 0; i < 5*sizeof(int); ++i)
ptr1[i] = i;
}
int *ptr2 = buf;
for(i = 0; i < 5; ++i)
printf("%d", ptr2[i]);
寻找关于此特定代码的如此简短(大概)的专家答案,最好是使用最少的标准引号,这就是我所追求的。我不是在对严格的别名规则进行长时间的解释之后,只是与这段代码有关的部分。如果答案能明确列举上面编号的问题,那就太好了。
还假设通用 CPU 没有整数陷阱值,我们还假设 int
是 32 位和二进制补码。
不,不是,但这只是因为分配了内存,并使用字符类型写入。
使用 malloc 分配内存。该对象没有声明 1 类型,因为它是用 malloc 分配的。因此该对象没有任何有效类型。
然后代码使用类型 char
访问和修改对象。由于类型是2char
并且没有对象具有有效类型被复制5,复制不会将有效类型设置为char
用于此访问和后续访问,但将有效类型设置为 char
,仅在访问期间 3。访问后,该对象不再具有有效类型。
然后类型int
用于访问并且只读取该对象。由于对象没有有效类型,因此在读取期间它变为 3 int
。访问后,对象不再具有有效类型。由于 int
显然与有效类型 int
兼容,因此定义了行为。
(假设读取的值不是 int
的陷阱表示。)
如果您使用与 int
不兼容的非字符类型访问和修改对象,则行为将是不确定的。
假设您的示例是(假设 sizeof(float)==sizeof(int)
):
int i;
void *buf = calloc(5, sizeof(float)); // buf initialized to 0
{
float *ptr1 = buf;
for(i = 0; i < 5*sizeof(float); ++i)
ptr1[i] = (float)i;
}
int *ptr2 = buf;
for(i = 0; i < 5; ++i)
printf("%d", ptr2[i]);
对象的有效类型,当 float
被写入时,在写入期间和所有后续对该对象的访问期间变为 float
类型修改2。当这些对象随后被 int
访问时,有效类型仍然是 float
,因为这些值只是被读取而不是被修改。之前使用 float
的写入将有效类型永久设置为 float
直到下一次写入该对象(在本例中没有发生)。类型 int
和 float
不兼容 4,因此行为未定义。
(以下所有文字均引自:ISO:IEC 9899:201x)
1(6.5 表达式 6)
访问其存储值的对象的有效类型是对象的声明类型(如果有)。 87) 分配的对象没有声明的类型。
2(6.5 表达式 6)
如果一个值通过类型 不是字符类型 的左值存储到没有声明类型的对象中,则左值的类型成为该对象的有效类型访问和不修改存储值的后续访问。
3(6.5 表达式 6)
对于没有声明类型的对象的所有其他访问,对象的有效类型只是用于访问的左值的类型。
4(6.5 表达式 8)
对象的存储值只能由具有以下之一的左值表达式访问
以下类型:88)
— 与对象的有效类型兼容的类型,
— 与对象的有效类型兼容的类型的限定版本,
— 一个类型,它是对应于有效类型的有符号或无符号类型
目的,
— 一种类型,它是对应于合格版本的有符号或无符号类型
对象的有效类型,
— 聚合或联合类型,其中包括上述类型之一
成员(递归地包括子聚合或包含联合的成员),或
— 一种字符类型。
5(6.5 表达式 6)
如果使用 memcpy 或 memmove 将值复制到没有声明类型的对象中,或者复制为字符类型的数组,则修改对象的有效类型用于该访问和不修改该值的后续访问 是从中复制值的对象的有效类型,如果它有的话。
没有。这并不违反严格的别名。
来自 the C Standard,6.2.5 类型 ,第 28 段:
A pointer to void
shall have the same representation and
alignment requirements as a pointer to a character type. 48
注意48,指的是脚注48:
48) The same representation and alignment requirements are
meant to imply interchangeability as arguments to functions,
return values from functions, and members of unions.
因此您可以通过 char *
指针访问 calloc()
的内存(假设您的 ptr
是 ptr1
)没有问题。
尽管这确实是额外的,因为 7.22.3 内存管理函数,第 1 段指出:
The pointer returned if the allocation succeeds is suitably aligned so
that it may be assigned to a pointer to any type of object with a
fundamental alignment requirement and then used to access such an
object or an array of such objects in the space allocated
因此您也可以通过 int
指针以及 char
指针安全地访问 calloc()
的内存。和一个 double
引导指针(假设您留在分配的内存范围内)。
问题:
下面这段代码是否违反了严格的别名规则?也就是说,是否允许智能编译器打印
00000
(或其他一些令人讨厌的效果),因为首先作为其他类型访问的缓冲区然后通过int*
?如果不是,将仅在大括号之前移动
ptr2
的定义和初始化(因此ptr2
已经定义,当ptr1
进入范围时) 打破它?如果不是,删除大括号(所以
ptr1
和ptr2
在同一范围内)会破坏它吗?如果是,代码怎么修改?
奖励问题:如果代码没问题,并且 2. 或 3. 也不要破坏它,如何更改它以破坏严格的别名规则(例如,将大括号循环转换为使用 int16_t
)?
int i;
void *buf = calloc(5, sizeof(int)); // buf initialized to 0
{
char *ptr1 = buf;
for(i = 0; i < 5*sizeof(int); ++i)
ptr1[i] = i;
}
int *ptr2 = buf;
for(i = 0; i < 5; ++i)
printf("%d", ptr2[i]);
寻找关于此特定代码的如此简短(大概)的专家答案,最好是使用最少的标准引号,这就是我所追求的。我不是在对严格的别名规则进行长时间的解释之后,只是与这段代码有关的部分。如果答案能明确列举上面编号的问题,那就太好了。
还假设通用 CPU 没有整数陷阱值,我们还假设 int
是 32 位和二进制补码。
不,不是,但这只是因为分配了内存,并使用字符类型写入。
使用 malloc 分配内存。该对象没有声明 1 类型,因为它是用 malloc 分配的。因此该对象没有任何有效类型。
然后代码使用类型 char
访问和修改对象。由于类型是2char
并且没有对象具有有效类型被复制5,复制不会将有效类型设置为char
用于此访问和后续访问,但将有效类型设置为 char
,仅在访问期间 3。访问后,该对象不再具有有效类型。
然后类型int
用于访问并且只读取该对象。由于对象没有有效类型,因此在读取期间它变为 3 int
。访问后,对象不再具有有效类型。由于 int
显然与有效类型 int
兼容,因此定义了行为。
(假设读取的值不是 int
的陷阱表示。)
如果您使用与 int
不兼容的非字符类型访问和修改对象,则行为将是不确定的。
假设您的示例是(假设 sizeof(float)==sizeof(int)
):
int i;
void *buf = calloc(5, sizeof(float)); // buf initialized to 0
{
float *ptr1 = buf;
for(i = 0; i < 5*sizeof(float); ++i)
ptr1[i] = (float)i;
}
int *ptr2 = buf;
for(i = 0; i < 5; ++i)
printf("%d", ptr2[i]);
对象的有效类型,当 float
被写入时,在写入期间和所有后续对该对象的访问期间变为 float
类型修改2。当这些对象随后被 int
访问时,有效类型仍然是 float
,因为这些值只是被读取而不是被修改。之前使用 float
的写入将有效类型永久设置为 float
直到下一次写入该对象(在本例中没有发生)。类型 int
和 float
不兼容 4,因此行为未定义。
(以下所有文字均引自:ISO:IEC 9899:201x)
1(6.5 表达式 6)
访问其存储值的对象的有效类型是对象的声明类型(如果有)。 87) 分配的对象没有声明的类型。
2(6.5 表达式 6)
如果一个值通过类型 不是字符类型 的左值存储到没有声明类型的对象中,则左值的类型成为该对象的有效类型访问和不修改存储值的后续访问。
3(6.5 表达式 6)
对于没有声明类型的对象的所有其他访问,对象的有效类型只是用于访问的左值的类型。
4(6.5 表达式 8)
对象的存储值只能由具有以下之一的左值表达式访问
以下类型:88)
— 与对象的有效类型兼容的类型,
— 与对象的有效类型兼容的类型的限定版本,
— 一个类型,它是对应于有效类型的有符号或无符号类型
目的,
— 一种类型,它是对应于合格版本的有符号或无符号类型
对象的有效类型,
— 聚合或联合类型,其中包括上述类型之一
成员(递归地包括子聚合或包含联合的成员),或
— 一种字符类型。
5(6.5 表达式 6)
如果使用 memcpy 或 memmove 将值复制到没有声明类型的对象中,或者复制为字符类型的数组,则修改对象的有效类型用于该访问和不修改该值的后续访问 是从中复制值的对象的有效类型,如果它有的话。
没有。这并不违反严格的别名。
来自 the C Standard,6.2.5 类型 ,第 28 段:
A pointer to
void
shall have the same representation and alignment requirements as a pointer to a character type. 48
注意48,指的是脚注48:
48) The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and members of unions.
因此您可以通过 char *
指针访问 calloc()
的内存(假设您的 ptr
是 ptr1
)没有问题。
尽管这确实是额外的,因为 7.22.3 内存管理函数,第 1 段指出:
The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated
因此您也可以通过 int
指针以及 char
指针安全地访问 calloc()
的内存。和一个 double
引导指针(假设您留在分配的内存范围内)。