【发布时间】:2017-01-07 04:37:56
【问题描述】:
我在结构中使用char 数组来保存一些通用数据,就像这样(输入类型可能是未知大小的结构,所以我不能只使用联合;这段代码被大大简化了) :
typedef struct {
char buf[256];
} data;
void data_set_int(data *d, int a) {
memcpy(d->buf, &a, sizeof(a));
}
int data_get_int(data *d) {
int ret;
memcpy(&ret, d->buf, sizeof(ret));
return ret;
}
void data_set_float(data *d, float a) {
memcpy(d->buf, &a, sizeof(a));
}
float data_get_float(data *d) {
float ret;
memcpy(&ret, d->buf, sizeof(ret));
return ret;
}
int main(void) {
data d;
data_set_int(&d, 3);
int int_result = data_get_int(&d);
data_set_float(&d, 10.0f);
float float_result = data_get_float(&d);
return 0;
}
如果我从不尝试编写 float,然后将数据读取为 int,反之亦然,那么这段代码在 C(99) 中是否定义良好?
使用 GCC 编译不会产生警告,并且运行代码会产生预期的行为(int_result == 3、float_result == 10.0f)。将 memcpy 更改为普通指针取消引用 (int ret = *(int *)d->buf) 也可以正常工作,不会出现警告。
我读过的所有关于严格别名的资料都说您可以将任何类型读取为char *(所以我认为这意味着set 应该没问题),但您不能像任何类型一样读取char *其他类型(不太确定get 是否可以)。我误解了规则吗?
【问题讨论】:
-
这很好,直到您到达
*(int *)d->buf。那时所有的赌注都被取消了,因为int可能有一个对齐约束,d->buf可能会也可能不会满足。 -
@AndrewSun 否。对齐限制意味着某些处理器和编译器会出错,而其他的则不会。严格的别名禁止通过两个不同 non-character 类型的指针取消引用同一块内存。你没有这样做。
-
@AndrewSun 另一方面,这不是一个聪明的方法。这正是
unions 的用途,它们将生成更高效的代码,因为它们will保证所有字段类型的对齐,因此不需要按字节复制。 -
@chux 在这种情况下字节布局重要吗?我是来自
int -> char[] -> int(或float -> char[] -> float)的memcpying,从未接触过缓冲区中的字节。 -
@AndrewSun 我读错了你的问题“如果我从不尝试编写浮点数然后将数据读取为 int 或反之亦然,这段代码在 C(99) 中是否定义良好?” .我错过了“从不”的部分。所以关于我以前的 cmets,never mind
标签: c undefined-behavior strict-aliasing