Подтвердить что ты не робот

Тип punning с void *, не нарушая строгого правила псевдонимов в C99

Недавно я столкнулся с строгим правилом псевдонимов, но у меня возникли проблемы с пониманием того, как использовать void * для выполнения функции punning без нарушения правила.

Я знаю, что это нарушает правило:

int x = 0xDEADBEEF;

short *y = (short *)&x;
*y = 42;

int z = x;

И я знаю, что могу безопасно использовать объединение в C99 для ввода типа:

union{
    int x;
    short y;
} data;

data.x = 0xDEADBEEF;
data.y = 42;

int z = data.x;

Но как я могу использовать void * для безопасного выполнения пиратства типа на C99? Правильно ли это:

int x = 0xDEADBEEF;

void * helper = (void *)&x;

short *y = (short *)helper;
*y = 42;

int z = x;

Я подозреваю, что код будет по-прежнему нарушать правило строгой псевдонижирования, поскольку память с переменным адресом x может быть изменена как с помощью x, так и с помощью y.

Если тип-punning равен undefined через void *, какова цель void * в C99?

4b9b3361

Ответ 1

void * не имеет ничего общего с типом. Его основными целями являются:

  • Разрешить общие операции выделения и освобождения, которые не заботятся о типе объекта, который хранит его вызывающий (например, malloc и free).

  • Чтобы вызывающий мог передать указатель на произвольный тип с помощью функции, которая будет передавать ее обратно через обратный вызов (например, qsort и pthread_create). В этом случае компилятор не может применять проверку типов; это ваша ответственность при написании вызывающего и обратного вызова, чтобы гарантировать, что обратный вызов обращается к объекту с правильным типом.

Указатели на void также используются в нескольких местах (например, memcpy), которые фактически работают с объектом в качестве наложенного представления unsigned char [] для объекта. Это можно рассматривать как тип-punning, но это не нарушение псевдонимов, потому что типам char разрешено псевдонизировать что-либо, чтобы получить доступ к его представлению. В этом случае unsigned char * также будет работать, но void * имеет то преимущество, что указатели автоматически преобразуются в void *.

В вашем примере, поскольку исходный тип int, а не объединение, не существует законного способа ввода слов и доступа к нему как short. Вместо этого вы можете скопировать значение x в объединение, выполнить четко определенный тип-punning, а затем скопировать его обратно. Хороший компилятор должен полностью опустить копию. В качестве альтернативы вы можете разбить запись на записи char, а затем это будет юридическое сглаживание.