предупреждение "разыменование типа-punned pointer нарушит правила строгого сглаживания"

Я использую код, где я приведу перечисление* к int*. Что-то вроде этого:--3-->

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 ответов


по порядку:

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

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

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

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


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

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


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

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);

вы заглянули в ответ ?

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


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

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

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