2020年12月11日更新:感谢@“一些程序员老兄”在评论中的建议。
我的根本问题是我们的团队正在实现动态类型存储引擎。我们分配多个字符数组[PAGE_SIZE]缓冲区16 对齐存储动态类型的数据(没有固定的结构)。出于效率原因,我们无法执行字节编码或分配额外的空间来使用memcpy
.
既然对齐方式已经确定了(即16),剩下的就是使用指针的强制转换来访问指定类型的对象,例如:
int main() {
// simulate our 16-aligned malloc
_Alignas(16) char buf[4096];
// store some dynamic data:
*((unsigned long *) buf) = 0xff07;
*(((double *) buf) + 2) = 1.618;
}
但我们团队对这个操作是否是未定义行为存在争议。
我读过很多类似的问题,例如
- 为什么 -Wcast-align 不会对 x86 上从 char* 到 int* 的转换发出警告? https://stackoverflow.com/questions/25762173/why-does-wcast-align-not-warn-about-cast-from-char-to-int-on-x86
- 如何在非对齐位置将 char 数组转换为 int? https://stackoverflow.com/questions/26995151/how-to-cast-char-array-to-int-at-non-aligned-position
- C 未定义的行为。严格的别名规则,还是不正确的对齐? https://stackoverflow.com/questions/46790550/c-undefined-behavior-strict-aliasing-rule-or-incorrect-alignment/46790815#46790815
- SEI CERT C.S EXP36-C https://wiki.sei.cmu.edu/confluence/display/c/EXP36-C.+Do+not+cast+pointers+into+more+strictly+aligned+pointer+types
但这些与我对C标准的解释不同,我想知道是否是我的误解。
主要的困惑在于该部分6.3.2.3 #7 http://port70.net/%7Ensz/c/c11/n1570.html#6.3.2.3p7 of C11:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未正确对齐 68) 对于引用类型,行为未定义。
68) 一般来说,“正确对齐”的概念是可传递的:如果指向类型 A 的指针与指向类型 B 的指针正确对齐,而类型 B 又与指向类型 C 的指针正确对齐,则指向类型的指针A 与指向 C 类型的指针正确对齐。
是否结果指针这里参考指针对象 or 指针值?
在我看来,我认为答案是指针对象,但更多的答案似乎表明指针值.
解释A:指针对象
我的想法如下:指针本身就是一个对象。根据6.2.5 #28 http://port70.net/%7Ensz/c/c11/n1570.html#6.2.5p28,不同的指针可能有不同的表示和对齐要求。因此,根据6.3.2.3 #7 http://port70.net/%7Ensz/c/c11/n1570.html#6.3.2.3p7,只要两个指针具有相同的对齐方式,就可以安全地转换它们,而不会出现未定义的行为,但不能保证它们可以被取消引用。
用程序表达这个想法:
#include <stdio.h>
int main() {
char buf[4096];
char *pc = buf;
if (_Alignof(char *) == _Alignof(int *)) {
// cast safely, because they have the same alignment requirement?
int *pi = (int *) pc;
printf("pi: %p\n", pi);
} else {
printf("char * and int * don't have the same alignment.\n");
}
}
解释B:指针值
然而,如果C11标准谈论的是指针值对于引用类型而不是指针对象。上面代码的对齐检查是没有意义的。
用程序表达这个想法:
#include <stdio.h>
int main() {
char buf[4096];
char *pc = buf;
/*
* undefined behavior, because:
* align of char is 1
* align of int is 4
*
* and we don't know whether the `value` of pc is 4-aligned.
*/
int *pi = (int *) pc;
printf("pi: %p\n", pi);
}
哪种解释是正确的?