могло ли логическое отрицание быть реализовано как побитовое отрицание в устаревших компиляторах? - PullRequest
0 голосов
/ 10 декабря 2018

Я только что нашел унаследованный код, который проверяет флаг следующим образом:

if( some_state & SOME_FLAG )

Пока все хорошо!
Но далее в коде я вижу неправильное отрицание

if( ! some_state & SOME_FLAG )

Насколько я понимаю, это интерпретируется как (! some_state) & SOME_FLAG, что, вероятно, является ошибкой, и gcc логически лает с -Wlogical-not-parentheses ...

Хотя в конечном итоге это могло бы сработать в прошлом, если когда-либо !some_stateбыл реализован как ~some_state неким устаревшим компилятором.Кто-нибудь знает, было ли это возможно?

EDIT

sme_state объявляется как int (предположительно, 32 бита, 2 дополнения в целевой архитектуре).
SOME_FLAG - константа, установленная на один бит 0x00040000, поэтому SOME_FLAG & 1 == 0

Ответы [ 4 ]

0 голосов
/ 10 декабря 2018

Давайте начнем с самой интересной (и наименее очевидной) части: gcc логически лает с -Wlogical-not-parentheses.Что это значит?

C имеет два разных оператора, которые имеют похожие символы (но отличаются по поведению и предназначены для совершенно разных целей) - &, который является побитовым AND, и &&, который являетсялогическое И.К сожалению, это привело к опечаткам, точно так же, как ввод =, когда вы имели в виду ==, может вызвать проблемы, поэтому некоторые компиляторы (GCC) решили предупредить людей о «& без использования скобок в качестве условия» (хотяэто совершенно законно), чтобы уменьшить риск опечаток.

Сейчас ...

Вы показываете код, который использует & (и не показывает код, который использует &&).Это означает, что some_state не является логическим значением и является числом.Более конкретно, это означает, что каждый бит в some_state может быть полностью независимым и не связанным.

Для примера давайте представим, что мы реализуем игру Pacman и нам нужен хороший компактный способ хранения картыдля каждого уровня.Мы решаем, что каждая плитка на карте может быть стеной или нет, может быть собранной точкой или нет, может быть пилюлей или нет, и может быть вишня или нет.Кто-то предполагает, что это может быть массив байтов, например (если предположить, что карта имеет ширину 30 плиток и высоту 20 плиток):

#define IS_WALL         0x01
#define HAS_DOT         0x02
#define HAS_POWER_PILL  0x04
#define HAS_CHERRY      0x08

uint8_t level1_map[20][30] = { ..... };

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

    if( level1_map[y][x] & IS_WALL == 0) {

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

    if( level1_map[y][x] & IS_WALL != 0) {

    if( !level1_map[y][x] & IS_WALL == 0) {

    if( level1_map[y][x] & IS_WALL == IS_WALL) {

..потому что не имеет значения, какой это.

Конечно (чтобы избежать опечаток) GCC может (или не может) предупреждать о некоторых из них.

0 голосов
/ 10 декабря 2018

Унаследованный компилятор не сможет реализовать ! как побитовое отрицание, потому что такой подход приведет к неверным результатам в ситуациях, когда отрицательное значение находится за пределами набора {0, 0xFF ... FF}.

Стандарт требует, чтобы результат !x приводил к нулю при любом ненулевом значении x.Следовательно, применение ! к, скажем, 1 даст 0xFF..FFFE, который не равен нулю.

Единственная ситуация, когда унаследованный код работал бы так, как задумано, это когда SOME_FLAG установлен в1.

0 голосов
/ 10 декабря 2018

Логическое отрицание и побитовое отрицание никогда не были эквивалентны.Никакой соответствующий компилятор не мог бы реализовать один как другой.Например, побитовое отрицание 1 не равно 0, поэтому ~1 != !1.

Это правда, что выражение ! some_state & SOME_FLAG эквивалентно (! some_state) & SOME_FLAG, поскольку логическое отрицание имеет более высокий приоритет, чем побитовое и.Это действительно подозрительно , но исходный код не обязательно содержит ошибку.В любом случае, более вероятно, что программа содержит ошибки в этом отношении, чем любая реализация C оценивала исходное выражение иначе, чем требует текущий стандарт, даже до стандартизации.

Поскольку выражения (! some_state) & SOME_FLAG и!(some_state & SOME_FLAG) иногда будет оценивать одно и то же значение - особенно если SOME_FLAG расширяется до 1 - также возможно, что даже если они неэквивалентны, их различия не проявляются во время фактического выполнения программы.

0 голосов
/ 10 декабря 2018

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

Существует очень мало причин для написания выражения типа (!foo & FLAG_BAR);результат равен просто !foo, если FLAG_BAR нечетное или всегда ноль, если оно четное.То, что вы нашли, почти наверняка просто ошибка.

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