Нарушает ли перемещение значений одного типа в другой тип строгий псевдоним?
-
20-09-2019 - |
Вопрос
Нарушает ли строгие правила псевдонимов перемещение элементов любого типа с помощью uint32_t, а затем их обратное чтение?Если да, то нарушает ли это также строгие правила псевдонимов для memcpy из массива uint32_ts в массив любого типа, а затем считывает элементы обратно?
Следующий пример кода демонстрирует оба случая:
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main(void) {
const char *strings[5] = {
"zero", "one", "two", "three", "four"
};
uint32_t buffer[5];
int i;
assert(sizeof(const char*) == sizeof(uint32_t));
memcpy(buffer, strings, sizeof(buffer));
//twiddle with the buffer a bit
buffer[0] = buffer[3];
buffer[2] = buffer[4];
buffer[3] = buffer[1];
//Does this violate strict aliasing?
const char **buffer_cc = (const char**)buffer;
printf("Test 1:\n");
for (i=0; i<5; i++)
printf("\t%s ", buffer_cc[i]);
printf("\n");
//How about this?
memcpy(strings, buffer, sizeof(strings));
printf("Test 2:\n");
for (i=0; i<5; i++)
printf("\t%s ", strings[i]);
printf("\n");
return 0;
}
Пожалуйста, не обращайте внимания на мое предположение о 32-битной платформе.Кроме того, если размеры элементов не совпадают с размером uint32_t, я знаю, что нужно дополнить их и скопировать правильное количество uint32_t.Мой вопрос сосредоточен на том, нарушает ли это строгий псевдоним.
Решение
Первый цикл делает технически нарушает строгий псевдоним — он получает доступ uint32_t
объекты через lvalue типа char *
.Однако трудно представить, как какой-либо оптимизатор может вызвать проблемы в этом конкретном случае.Если вы немного изменили его, чтобы сделать что-то вроде:
printf("\t%s ", buffer_cc[0]);
buffer[0] = buffer[3];
printf("\t%s ", buffer_cc[0]);
Ты мощь увидеть одну и ту же строку, напечатанную дважды, поскольку оптимизатор будет иметь право только загружать buffer_cc[0]
в регистр один раз, потому что вторая строка изменяет только объект типа uint32_t
.
Второй цикл, который memcpy
Если они вернутся, все в порядке.
Другие советы
buffer_cc[0]
и strings[3]
(например) являются указателями, которые ссылаются на одну и ту же ячейку памяти, но имеют один и тот же тип, поэтому не нарушают строгий псевдоним. buffer[0]
не является указателем, поэтому не нарушает строгий псевдоним.Оптимизация псевдонимов возникает при разыменовании указателей, поэтому я не ожидал бы, что это вызовет проблемы.
Как вы намекаете в коде и последнем абзаце вашего вопроса, настоящая проблема в примере кода возникает, когда указатели и uint32_t имеют разные размеры.
Кроме того, вы всегда можете использовать псевдоним char*
указать на другой тип, не нарушая строгий псевдоним, но не наоборот.