【发布时间】:2019-03-19 12:36:58
【问题描述】:
我有以下程序,通过将 8 位缓冲区转换为 32 位和 64 位值,以一种看似快速的方式初始化两个缓冲区。
#include <stdio.h>
#include <stdint.h>
typedef struct {
uint32_t a[2];
uint16_t b[4];
} ctx_t;
void inita(ctx_t *ctx, uint8_t *aptr)
{
*(uint64_t *) (ctx->a) = *(uint64_t *) (aptr);
}
void initb(ctx_t *ctx, uint8_t *bptr)
{
for (int i = 0; i < 2; i++) {
*((uint32_t *) (ctx->b) + i) = *(uint32_t *) (bptr);
}
}
int main()
{
uint8_t a[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
uint8_t b[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
ctx_t ctx;
inita(&ctx, a);
initb(&ctx, b);
printf("a: ");
for (int i = 0; i < 8; i++) {
printf("%02x", a[i]);
}
printf("\nb: ");
for (int i = 0; i < 8; i++) {
printf("%02x", b[i]);
}
printf("\n");
}
使用 GCC 版本 8.2.1 进行编译时,我收到以下警告消息:
> gcc -std=c99 -Wall -Wextra -Wshadow -fsanitize=address,undefined -O2 main.c
main.c: In function ‘inita’:
main.c:11:3: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
*(uint64_t *) (ctx->a) = *(uint64_t *) (aptr);
^~~~~~~~~~~~~~~~~~~~~
我读到了严格的别名规则,如果打破这个规则可能会出错,这是有道理的。 但是,为什么我在 initb() 函数中没有收到相同的警告?在这里,我还将指针和缓冲区转换为其他大小而不是声明的大小。
程序运行并产生预期的输出:
a: 0123456789abcdef
b: 0123456789abcdef
如果我修复警告,请执行以下操作:
void inita(ctx_t *ctx, uint8_t *aptr)
{
*(uint32_t *) (ctx->a) = *(uint32_t *) (aptr);
*(uint32_t *) (ctx->a + 1) = *(uint32_t *) (aptr + 4);
}
然后,我现在得到与以前相同的结果,但没有警告。
我的代码中是否仍然存在别名问题(由于 initb)或者这样做是否安全?
【问题讨论】:
-
我在 gcc 5.4.0 上得到了同样的结果。
-
尝试在 initb() 中展开循环,您将看到与 inita() 相同的警告。所以当涉及到循环变量时,只是 gcc 没有识别出问题
标签: c strict-aliasing