У меня есть gcc ошибка оптимизации или проблема кода C?

В Java все переменные, которые вы объявляете, на самом деле являются «ссылками» на объекты (или примитивы), а не самими объектами.

При попытке выполнить один метод объекта , ссылка просит живой объект выполнить этот метод. Но если ссылка ссылается на NULL (ничего, нуль, void, nada), то нет способа, которым метод будет выполнен. Тогда runtime сообщит вам об этом, выбросив исключение NullPointerException.

Ваша ссылка «указывает» на нуль, таким образом, «Null -> Pointer».

Объект живет в памяти виртуальной машины пространство и единственный способ доступа к нему - использовать ссылки this. Возьмем этот пример:

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

И в другом месте вашего кода:

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

Это важно знать - когда больше нет ссылок на объект (в пример выше, когда reference и otherReference оба указывают на null), тогда объект «недоступен». Мы не можем работать с ним, поэтому этот объект готов к сбору мусора, и в какой-то момент VM освободит память, используемую этим объектом, и выделит другую.

10
задан Eddie 1 March 2009 в 03:47
поделиться

9 ответов

Используйте флаг компилятора-fno-strict-aliasing.

Со строгим включенным искажением, как это по умолчанию для, по крайней мере-O3 в строке:

size_t t = *((size_t*)&f);

компилятор предполагает, что size_t* НЕ указывает на ту же область памяти как float*. Насколько я знаю, это - совместимое стандартами поведение (соблюдение со строгими правилами искажения в ANSI стандартный запуск вокруг gcc-4, как Thomas Kammeyer указал).

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

Другими словами, попробуйте это (не может протестировать его самостоятельно прямо сейчас, но я думаю, что это будет работать):

size_t t = *((size_t*)(char*)&f);
18
ответ дан 3 December 2019 в 14:12
поделиться

В стандарте C99 это охвачено следующим правилом в 6.5-7:

Объект должен иметь свою хранимую сумму полученной доступ только по lvalue выражению, которое имеет один из следующих types:73),

  • тип, совместимый с эффективным типом объекта,

  • квалифицированная версия типа, совместимого с эффективным типом объекта,

  • тип, который является или неподписанным типом со знаком, соответствующим эффективному типу объекта,

  • тип, который является или неподписанным типом со знаком, соответствующим квалифицированной версии эффективного типа объекта,

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

  • тип символов.

Последний объект - то, почему кастинг сначала к (символ*) работает.

6
ответ дан 3 December 2019 в 14:12
поделиться

Это больше не позволяется согласно правилам C99 об искажении указателя. Указатели двух различных типов не могут указать на то же местоположение в памяти. Исключениями к этому правилу являются пустые и символьные указатели.

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

размер size_t = (size_t) (f);//это работает

Об этом обычно сообщают как ошибка, но на самом деле действительно является функцией, которая позволяет оптимизаторам работать более эффективно.

В gcc можно отключить это с переключателем компилятора. Я верю-fno_strict_aliasing.

5
ответ дан 3 December 2019 в 14:12
поделиться

Это - плохой код C :-)

Проблематичная часть - то, что Вы получаете доступ к одному объекту плавания типа путем кастинга его к целочисленному указателю и разыменования его.

Это нарушает правило искажения. Компилятор является бесплатным предположить, что указатели на различные типы, такие как плавание или интервал не накладываются в памяти. Вы сделали точно это.

То, что видит компилятор, - то, что Вы вычисляете что-то, храните его в плавании f и никогда не получаете доступ к нему больше. Скорее всего, компилятор удалил часть кода, и присвоения никогда не происходило.

Разыменование через Ваш size_t указатель будет в этом случае возвращать немного неинициализированного мусора из стека.

Можно сделать две вещи к обходному решению это:

  1. используйте объединение с плаванием и size_t участником и сделайте кастинг через трамбовку типа. Не хороший, но работы.

  2. используйте memcopy для копирования содержания f в size_t. Компилятор достаточно умен, чтобы обнаружить и оптимизировать этот случай.

5
ответ дан 3 December 2019 в 14:12
поделиться

Это - плохой код C. Ваш бросок повреждает C искажающие правила, и оптимизатор свободен, делают вещи, которые взламывают этот код. Вы, вероятно, найдете, что GCC имеет cheduled чтение size_t перед записью с плавающей точкой (для сокрытия конвейерной задержки fp).

Можно установить переключатель-fno-strict-aliasing или использовать объединение или reinterpret_cast для иного толкования значению совместимым стандартами способом.

1
ответ дан 3 December 2019 в 14:12
поделиться

Почему Вы думали бы, что t должен быть 0?

Или, больше accuractely формулировало, "Почему Вы думали бы, что двоичное представление нуля с плавающей точкой совпадет с двоичным представлением целочисленного нуля?"

3
ответ дан 3 December 2019 в 14:12
поделиться

- O3 не считают "нормальным",-O2 обычно является верхним порогом кроме, возможно, для некоторых мультимедийных приложений.

Некоторые приложения не могут даже пойти, что далеко, и умирают, если Вы идете вне-O1.

Если у Вас есть достаточно новый GCC (я нахожусь на 4,3 здесь), он может поддерживать эту команду

  gcc -c -Q -O3 --help=optimizers > /tmp/O3-opts

Если Вы будете осторожны, то Вы возможно сможете пройти тот список и найти данную исключительную оптимизацию, которую Вы включаете который причины эта ошибка.

От man gcc :

  The output is sensitive to the effects of previous command line options, so for example it is possible to find out which
       optimizations are enabled at -O2 by using:

               -O2 --help=optimizers

       Alternatively you can discover which binary optimizations are enabled by -O3 by using:

               gcc -c -Q -O3 --help=optimizers > /tmp/O3-opts
               gcc -c -Q -O2 --help=optimizers > /tmp/O2-opts
               diff /tmp/O2-opts /tmp/O3-opts | grep enabled
-1
ответ дан 3 December 2019 в 14:12
поделиться

В стороне выравнивание указателя, Вы ожидаете что sizeof (size_t) == sizeof (плавание). Я не думаю, что это (на 64-разрядном Linux size_t, должно быть 64 битами, но 32 бита плавающие), означая, что Ваш код считает что-то неинициализированное.

1
ответ дан 3 December 2019 в 14:12
поделиться

Я протестировал Ваш код с: "i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (сборка Apple Inc. 5465)"

и не было никакой проблемы. Вывод:

t should be 0 but is 0

Таким образом, нет ошибки в Вас кода. Это не означает, что это - хороший код. Но я добавил бы returntype основной функции и "возврата 0"; в конце функции.

-2
ответ дан 3 December 2019 в 14:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: