У меня есть следующая программа, в которой я инициализирую два буфера, казалось бы, быстрым способом, приводя 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) или это безопасно?