Gcc или другие компиляторы автоматически преобразуют поразрядно или в логические или при использовании в условных выражениях? - PullRequest
3 голосов
/ 26 августа 2011

Если у меня есть оператор C с логическим оператором ИЛИ ||:

if (isFoo() || isBar())
    blah();

Я знаю, что сгенерированный компилятором код не будет выполняться isBar(), если isFoo() вернет true.

Как насчет побитового оператора ИЛИ |?

if (isFoo() | isBar())
    blah();

Скорее всего, это неаккуратная запись, или если писатель требует, чтобы isBar() и isFoo() были выполнены из-за этих функций 'побочные эффекты, то они должны выражать свои намерения более четко.Или, может быть, я ошибаюсь, и это приемлемая идиома C / C ++.

Тем не менее, приличный компилятор действительно сгенерирует временную переменную для выполнения побитовой обработки или обработки возвращаемых значений isFoo() и isBar() когда включены оптимизации?Или вместо этого он преобразует побитовую операцию ИЛИ в логическую операцию ИЛИ, чтобы разрешить короткое замыкание логического выражения, чтобы предотвратить вызов isBar()?

Ответы [ 4 ]

9 голосов
/ 26 августа 2011

Компилятор может оптимизировать «или» по своему усмотрению, но программа должна вести себя так, как если бы оба вызова функции действительно происходили, и они могли бы происходить в любом порядке .Это действительно укусило меня однажды, когда я наивно изменил || на |, потому что мне нужно было выполнить оба вызова, но я забыл, что правый вызов может произойти раньше, чем левый, а правый зависит отрезультаты левого ... и ошибка не появлялась, пока кто-то не решил попробовать скомпилировать мой код с pcc вместо gcc.Поэтому я советую быть осторожнее с подобными вещами и четко записать, что вы имеете в виду.

Наконец, обратите внимание, что я сказал «как будто оба вызова функции действительно происходят», потому что в случае, когда компилятор может определитьчто функция не имеет побочных эффектов, она может оптимизировать правую сторону, если левая сторона приведет к ненулевому значению.

3 голосов
/ 26 августа 2011

Нет, это не разумная практика кодирования. Помимо семантики короткого замыкания и упорядочения ||, он выполняет операцию, отличную от |.

|| возвращает 1, если любой из операндов ненулевой, 0, если оба равны нулю.

| возвращает поразрядно или его операндов.

Как это происходит, значение истинности результата будет таким же (я понял это, когда набирал этот ответ). Но рассмотрим соответствующие операторы && против &. Это:

if (isFoo() && isBar())

будет истинным, если и только если обе функции возвращают ненулевое значение, но это:

if (isFoo() & isBar())

будет истинным в том и только в том случае, если битовая и результаты отличны от нуля. Если isFoo() возвращает 1, а isBar() возвращает 2 (оба истинных результата), то isFoo & isBar() будет 0 или false.

Обратите внимание, что функции is*(), объявленные в <ctype.h>, указываются только для возврата ненулевого значения для true; они могут и действительно возвращают значения, отличные от 0 или 1.

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

bool is_foo = isFoo();
bool is_bar = isBar();
if (is_foo && is_bar) ...
3 голосов
/ 26 августа 2011

"Тем не менее, будет ли приличный компилятор фактически генерировать временную переменную для выполнения побитового преобразования или возврата значений isFoo () и isBar () при включенной оптимизации?"

Да.Короткое замыкание не распространяется на побитовые операторы.

0 голосов
/ 26 августа 2011

Здесь мы говорим прямо на C, поэтому у нас должно быть что-то вроде

typedef int BOOL;
BOOL isFoo();
BOOL isBAr();

Это потому, что только C ++ (а не C) имеет логический тип.

Теперь, когда мы оцениваем isFoo() || isBar() компилятор знает, что поскольку мы используем логический или если isFoo () вернет true, весь оператор будет иметь значение true независимо от значения isBar (), поэтому компилятор может безопасно замкнуть накороткоisBar ().

Однако, когда мы оцениваем isFoo () |isBar () мы эффективно ИЛИ два целых числа.Компилятор не может знать, что isFoo () и isBar () могут возвращать только 0 или 1 и, следовательно, должны будут вычислять оба выражения, потому что даже если isFoo () возвращает 1, конечный результат может отличаться (например, 3).

C ++ имеет тип bool, и если isFoo() и isBar() возвращают bool s, компилятору действительно не нужно оценивать isBar().Я не эксперт по спецификации C ++, но я думаю, что поскольку побитовые операторы не имеют смысла для bool типов, bool s будет повышен до int и оценен соответственно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...