Тип punning, char[] и разыменование


У меня есть структура, которая предназначена для хранения пользовательских данных (т. е. из плагина). Он имеет такой char[] с заданным максимальным размером для хранения этих данных.

struct A
{
    // other members omitted
    // data meant to be type punned, only contains PODs
    char data[256];
};

Затем есть пример структуры пользователя, которая имеет статическую функцию для приведения себя из A.

struct B
{
    int i;
    double d;

    static B& FromA_ref(A& a)
    {
        // static_assert that sizeof(B) < sizeof(A::data)
        return * reinterpret_cast<B*>(a.data);
    }
};

Я компилирую с g++ -O3 -std=c++0x -Wall -o test test.cpp (GCC 4.6.1).

Это вызывает предупреждение dereferencing type-punned pointer will break strict-aliasing rules. Я думал, что это будет нормально, так как я использовал char[] в качестве хранилища, которое, как я думал, будет следовать тем же правилам, что и char*. Я нахожу его странно, что этого не происходит. Что ж,... Я не могу изменить это прямо сейчас, так что давайте двигаться дальше.

Теперь рассмотрим следующий метод:

struct B
{
    ....
    static B* FromA_ptr(A& a)
    {
        // static_assert that sizeof(B) < sizeof(A::data)
        return reinterpret_cast<B*>(a.data);
    }
}

Поскольку я ничего не разыменовываю здесь, GCC не выводит никакого предупреждения. То же самое происходит, когда я использую свой указатель на B позже.

A a;
auto b = B::FromA_ptr(a);
b->i = 2; // no warnings.

Но безопасно ли это делать ?У меня такое чувство, что я стараюсь обойти проблему, а не решить ее. Для меня -> все еще разыменовывает переменную как-то.

Кроме того, есть ли лучший способ достичь эффекта ? Т. е. получить модифицируемую ссылку (или указатель), приведенную из хранилища внутри другой структуры ? (Union не будет работать, так как набор хранимых типов не известен, когда A определен, и некоторые могут быть добавлены через плагины, memcpy заставит меня копировать данные туда и обратно, хотя это, кажется, единственный безопасный способ до сих пор)

1 5

1 ответ:

Ответ-Нет, это небезопасно (см. Этот так что вопрос)

GCC будет считать, что указатели не могут иметь псевдонимов. Например, если вы назначаете через один, а затем читаете из другого, GCC может, в качестве оптимизации, изменить порядок чтения и записи - я видел, как это происходит в производственном коде, это не очень приятно отлаживать.

Атрибут((may_alias )) по используемым типам, вероятно, ближе всего вы можете получить к отключению предположения для конкретного раздел кода.