Введите каламбур, 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 заставит меня копировать данные туда и обратно, хотя это кажется единственным безопасным способом, поэтому далеко)


person J.N.    schedule 03.04.2012    source источник
comment
Я не эксперт, но вы ищете атрибут __may_alias__? blog.worldofcoding.com/2010/02/   -  person Russell Borogove    schedule 03.04.2012
comment
В любом случае, как вы решаете проблему выравнивания?   -  person curiousguy    schedule 11.07.2012
comment
@curiousguy: пока не знаю, но должен. На x86 я думаю, что в худшем случае я могу ухудшить производительность. Я надеюсь, что __attribute__((aligned(8))); будет достаточно, так как я не пытаюсь ничего автоматически векторизовать. Вероятно, may_alias уже говорит GCC обратить внимание на выравнивание, прежде чем делать слишком много странных вещей.   -  person J.N.    schedule 12.07.2012
comment
@curiousguy: простой подход должен быть union { char dat[theSize]; typeWithWorstAlignment dummy; }. Это позаботится обо всем, кроме проблем с псевдонимами, для которых Стандарт не предлагает решения. Учитывая отсутствие какого-либо решения проблем с псевдонимами, я нахожу странным, что стандарт C11 включает директивы выравнивания, которые на самом деле бесполезны, кроме в обстоятельствах, связанных с каламбуром типов.   -  person supercat    schedule 13.02.2017


Ответы (1)


Ответ - нет, это небезопасно (см. ">ТАК вопрос)

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

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

person Attila    schedule 03.04.2012