предупреждение «Разыменование указателя типа будет нарушать правила строгого алиасинга» - PullRequest
53 голосов
/ 12 ноября 2010

Я использую код, в котором я приводил enum * к int *. Примерно так:

enum foo { ... }
...
foo foobar;
int *pi = reinterpret_cast<int*>(&foobar);

При компиляции кода (g ++ 4.1.2) я получаю следующее предупреждение:

dereferencing type-punned pointer will break strict-aliasing rules

Я погуглил это сообщение и обнаружил, что оно происходит только при включенной строгой оптимизации псевдонимов. У меня есть следующие вопросы:

  • Если я оставлю код с этим предупреждением, будет ли он генерировать потенциально неправильный код?
  • Есть ли способ обойти эту проблему?
  • Если нет, возможно ли отключить строгое алиасинг из исходного файла (потому что я не хочу отключать его для всех исходных файлов и не хочу создавать отдельное правило Makefile для этот исходный файл)?

И да, мне действительно нужен такой псевдоним.

Ответы [ 5 ]

57 голосов
/ 12 ноября 2010

В заказе:

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

  • Несколько.Вы можете использовать объединение для представления памяти, которую вам нужно переосмыслить.Вы можете использовать reinterpret_cast.Вы можете разыграть через char * в точке, где вы переосмыслите память - char * определены как имеющие возможность псевдонима чего угодно.Вы можете использовать тип, который имеет __attribute__((__may_alias__)).Вы можете отключить предположения псевдонимов глобально, используя -fno-strict-aliasing.

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

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

11 голосов
/ 06 ноября 2013

Вы можете использовать следующий код для приведения ваших данных:

template<typename T, typename F>
struct alias_cast_t
{
    union
    {
        F raw;
        T data;
    };
};

template<typename T, typename F>
T alias_cast(F raw_data)
{
    alias_cast_t<T, F> ac;
    ac.raw = raw_data;
    return ac.data;
}

Пример использования:

unsigned int data = alias_cast<unsigned int>(raw_ptr);
11 голосов
/ 12 ноября 2010

Но зачем ты это делаешь?Он сломается, если sizeof (foo)! = Sizeof (int).То, что перечисление похоже на целое число, не означает, что оно хранится как единое целое.

Так что да, оно может генерировать «потенциально» неправильный код.

5 голосов
/ 12 ноября 2010

Вы изучили этот ответ ?

Строгое правило псевдонимов делает эту настройку недопустимой, два несвязанных типа не могут указывать на одну и ту же память. Только char * имеет эту привилегию .К сожалению, вы все еще можете кодировать таким образом, возможно, получить некоторые предупреждения, но скомпилируйте его нормально.

2 голосов
/ 12 ноября 2010

Строгий псевдоним - это опция компилятора, поэтому вам нужно отключить его из make-файла.

И да, он может генерировать неправильный код.Компилятор будет эффективно предполагать, что foobar и pi не связаны вместе, и будет предполагать, что *pi не изменится, если foobar изменилось.

Как уже упоминалось, используйте static_castвместо этого (и без указателей).

...