Различное поведение оператора сдвига с -O2 и без - PullRequest
8 голосов
/ 04 марта 2011

Без -O2 этот код печатает 84 84, с флагом O2 вывод равен 84 42. Код был скомпилирован с использованием gcc 4.4.3. на 64-битной платформе Linux. Почему вывод для следующего кода отличается?

Обратите внимание, что при компиляции с -Os вывод будет 0 42

#include <iostream>
using namespace std;

int main() {
    long long n = 42;
    int *p = (int *)&n;
    *p <<= 1;
    cout << *p << " " << n << endl;
    return 0;
}

Ответы [ 3 ]

19 голосов
/ 04 марта 2011

Когда вы используете оптимизацию с gcc, он может использовать определенные предположения, основанные на типе выражений, чтобы избежать повторения ненужных чтений и разрешить сохранение переменных в памяти.

Ваш код имеет неопределенное поведение, поскольку вы приводите указательна long long (который gcc допускает в качестве расширения) на указатель на int и затем манипулирует указанным на объект, как если бы это был int.Указатель на int обычно не может указывать на объект типа long long, поэтому gcc может допустить, что операция, записывающая в int (через указатель), не повлияет на объект, имеющий тип long long.

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

Конкретный переключатель и документация для чтения - -fstrict-aliasing.

6 голосов
/ 04 марта 2011

Вы нарушаете строгий псевдоним.Компиляция с -Wall должна дать вам предупреждение dereferencing type-punned pointer.См. Например http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

1 голос
/ 04 марта 2011

Я получаю те же результаты с GCC 4.4.4 в Linux / i386.

Поведение программы не определено, поскольку оно нарушает правило строгого наложения имен.

...