【发布时间】:2019-03-20 02:52:09
【问题描述】:
标准允许我们将指向对象类型的指针相互转换,前提是它们适当对齐。 6.3.2.3(p7):
指向对象类型的指针可以转换为指向对象类型的指针 不同的对象类型。如果结果指针不正确 aligned68) 对于引用的类型,行为未定义。
标准允许我们将对象表示复制到char[sizeof(the_object_type)]6.2.6.1(p4):
可以将值复制到 unsigned char [n] 类型的对象中 (例如,通过 memcpy);结果的字节集称为对象 值的表示。
此外,该标准明确指出
具有相同对象表示的两个值(NaN 除外) 比较相等,但比较相等的值可能有不同的对象 表示。
考虑以下代码:
struct contains_64_t{
uint64_t value;
};
int main(int args, const char *argv[]){
_Alignas(struct contains_64_t)
char buf_2_64t[2 * sizeof(struct contains_64_t)];
struct contains_64_t c64_1;
c64_1.value = 1;
struct contains_64_t c64_2;
c64_2.value = 2;
memcpy(buf_2_64t, &c64_1, sizeof(c64_1));
memcpy(buf_2_64t + sizeof(c64_1), &c64_2, sizeof(c64_2));
//suitably aligned, ok
struct contains_64_t *c64_ptr = (struct contains_64_t*) buf_2_64t;
printf("Value %"PRIu64"\n", c64_ptr -> value);
}
问题:这样写代码是不是很迂腐?如果不是,这样做可能会遇到什么样的问题?
据我所知,
我们可以将char* 转换为struct contains_64_t,因为它是适当对齐的。但问题是buf 的声明类型是char[2 * sizeof(struct contains_64_t)]。所以正式地说,我们不能通过struct contains_64_t * 类型的左值访问buf。
但这会很奇怪,因为我们已经适当地对齐了指针和字面上相同的对象表示。我们当然可以声明struct contains_64_t buf[2];,但如果struct 包含可变长度数组
UPD:如果我们假设我们正在使用 GCC 进行编译,那么这样的缓冲区对齐是否就足够了?
【问题讨论】:
-
对齐不是这里潜在的 UB,通过抗锯齿可能是
struct contains_64_t *c64_ptr = (struct contains_64_t*) buf_2_64t;的一个问题。printf("Value = %lu\n", c64_ptr -> value);肯定是带有 32 位long的 UB。 -
@chux 为什么要转换为适当对齐的指针 UB?或者你的意思是UB是由
printf的错误使用引起的?固定。 -
一些 1) casting 不是 UB 的问题,anti-aliasing UB 可能适用于此。 2)
%lu用于unsigned long,不一定是uint64_t。 -
@chux 同意
2)。将其替换为在inttypes.h中定义的PRIu64。 -
@chux 无论如何,如果我们不将指针对齐到堆栈分配的缓冲区,我们将无法转换它。所以 UB 也将出现在
struct contains_64_t *c64_ptr = (struct contains_64_t*) buf_2_64t;否则标准允许在6.2.3.2(p7)进行此类转换。
标签: c language-lawyer type-punning