Msgstr "разглаживающий тип-караульный указатель нарушит правила строгого сглаживания"

Я использую код, в котором я передавал 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

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

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

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

Ответы

Ответ 1

В порядке:

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

Ответ 4

Вы просмотрели этот ответ?

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

Ответ 5

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

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

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