Простое приведение к выполнению необработанной копии переменной нарушает строгий псевдоним? - PullRequest
3 голосов
/ 20 февраля 2011

Я читал о строгом псевдониме довольно много в последнее время. Стандарты C / C ++ говорят, что следующий код является недопустимым (неопределенное поведение, чтобы быть правильным), так как компилятор может иметь значение a, кэшированное где-то, и не признает, что ему нужно обновить значение при обновлении b ;

float *a;
...
int *b = reinterpret_cast<int*>(a);
*b = 1;

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

float *a;
...
char *b = reinterpret_cast<char*>(a);
*b = 1;

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

float a = 2.4;
int32_t b = reinterpret_cast<int&>(a);

То, что я хочу сделать, это просто скопировать необработанное значение a, поэтому строгий псевдоним не должен применяться. Здесь есть возможная проблема, или просто GCC слишком осторожен с этим?

EDIT

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

EDIT2

int32_t b = *reinterpret_cast<int*>(&a); также не работает.

решаемые

Кажется, это ошибка в GCC .

Ответы [ 3 ]

3 голосов
/ 20 февраля 2011

Если вы хотите скопировать немного памяти, вы можете просто сказать компилятору сделать это:

Редактировать: добавлена ​​функция для более удобочитаемого кода:

#include <iostream>
using std::cout; using std::endl;
#include <string.h>

template <class T, class U>
T memcpy(const U& source)
{
    T temp;
    memcpy(&temp, &source, sizeof(temp));
    return temp;
}

int main()
{
    float f = 4.2;
    cout << "f: " << f << endl;

    int i = memcpy<int>(f);
    cout << "i: " << i << endl;
}

[Код] [Обновленный код]

Редактировать: Как правильно указал пользователь / GMan в комментариях, полнофункциональная реализация может проверить, что T и U являются POD . Тем не менее, учитывая, что имя функции по-прежнему memcpy, возможно, можно полагаться на то, что ваши разработчики воспринимают ее как имеющую те же ограничения, что и исходная memcpy. Это зависит от вашей организации. Также используйте размер пункта назначения, а не источника. (Спасибо, Оли.)

1 голос
/ 20 февраля 2011

В основном строгие правила псевдонимов таковы, что «доступ к памяти с типом, отличным от объявленного, не определен, за исключением массива символов». Итак, gcc не слишком осторожен.

0 голосов
/ 20 февраля 2011

Если это то, что вам нужно делать часто, вы также можете просто использовать объединение, которое IMHO более читабельно, чем приведение или memcpy для этой конкретной цели:

union floatIntUnion {
  float a;
  int32_t b;
};

int main() {
  floatIntUnion fiu;
  fiu.a = 2.4;
  int32_t &x = fiu.b;
  cout << x << endl;
}

Я понимаю, что это на самом деле не отвечает на ваш вопрос о строгом псевдониме, но я думаю, что этот метод делает код более чистым и показывает ваши намерения лучше.

И также осознайте, что даже при правильном выполнении копий нет гарантии, что int, который вы получите, будет соответствовать тому же float на других платформах, поэтому считайте любые сетевые / файловые операции ввода / вывода этих операций с плавающей запятой / если вы планируете создать кроссплатформенный проект.

...