Почему ядро ​​Linux использует двойное логическое отрицание вместо приведения к bool?

Учитывая, что x является переменной типа int с числом 5 в качестве значения, рассмотрим следующий оператор:

int y = !!x;

Вот что, я думаю, происходит: x неявно приводится к bool и выполняется первое отрицание, после этого выполняется последнее отрицание, поэтому выполняется приведение и два отрицания.

Мой вопрос заключается не только в том, что приведение к bool (выполнение int y = (bool)x; вместо int y = !!x) происходит быстрее, чем при использовании двойного отрицания, поскольку вы сохраняете два отрицания от выполнения.

Я могу ошибаться, потому что я часто вижу двойное отрицание в ядре Linux, но я не понимаю, где моя интуиция ошибается, может быть, вы можете мне помочь.


person Garmekain    schedule 06.07.2018    source источник
comment
Когда ядро ​​Linux было впервые написано, в C не было логического значения.   -  person jwdonahue    schedule 07.07.2018
comment
Обмани меня дважды, позор мне.   -  person jxh    schedule 07.07.2018
comment
Не существует такого понятия, как «неявное приведение». Приведение — это синтаксическая конструкция. x могло быть неявно преобразовано в bool, но это не происходит в !x, имеющем тип int, и в !!x, также имеющем тип int.   -  person Pascal Cuoq    schedule 07.07.2018


Ответы (3)


Когда Linux был впервые написан, не было логического типа. Язык C рассматривал все, что не равно нулю, как истинное в булевых выражениях. Таким образом, 7, -2 и 0xFF являются «истинными». Нет логического типа для приведения. Трюк с двойным отрицанием гарантирует, что результатом будет либо нуль, либо любой битовый шаблон, который авторы компилятора выбрали для представления true в логических выражениях. Когда вы отлаживаете код и просматриваете значения памяти и регистров, легче распознать истинные значения, когда все они имеют одинаковые битовые комбинации.

Приложение: согласно проекту стандарта C89, раздел 3.3.3.3:

Результат оператора логического отрицания ! равно 0, если значение его операнда сравнивается с неравным 0, 1, если значение его операнда сравнивается с равным 0. Результат имеет тип int . Выражение !E эквивалентно (0==E).

Таким образом, хотя на заре ОС Linux не было логического типа, двойное отрицание давало бы либо 0, либо 1 (благодаря Gox за указание на это), в зависимости от правдивости выражения. Другими словами, любой битовый шаблон в диапазоне от INT_MIN..-1 до 1..INT_MAX дал бы 1, а нулевой битовый шаблон говорит сам за себя.

person jwdonahue    schedule 06.07.2018
comment
@Ivan, спасибо за редактирование, моя lesdixya, должно быть, сработала, потому что я ясно помню, как просматривал файл ‹stdint.h›, прежде чем писать эти константы! ;( - person jwdonahue; 14.07.2018

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

Как уже упоминали @jwdonahue и @Gox, это неверная причина. В C не было логического значения, когда было написано ядро ​​Linux, поэтому приведение к логическому типу было невозможным.

Что касается эффективности, то оба они эквивалентны, потому что компиляторы могут легко понять это. См. https://godbolt.org/g/ySo6K1.

bool cast_to_bool_1(int x) {
    return !!x;
}

bool cast_to_bool_2(int x) {
    return (bool) x;
}

Обе функции компилируются в одну и ту же сборку, которая использует инструкцию test для проверки, равен ли аргумент нулю или нет.

test edi, edi   // checks if the passed argument is 0 or not
setne al        // set al to 0 or 1 based on the previous comparison
ret             // returns the result
person lakshayg    schedule 06.07.2018
comment
Требуется использовать 7 символов только в том случае, если вы используете _Bool вместо bool. Но для bool требуется #include <stdbool.h>. - person jxh; 07.07.2018

C язык в отличие от других языков не имеет bool типа. bool в C на самом деле определено в stdbool.h, который не включен во многие проекты C. Ядро Linux является одним из таких проектов, и было бы сложно просмотреть код Linux и обновить все, чтобы использовать bool сейчас. Вот почему ядро ​​Linux не использует bool в C.

почему !!x? Это делается для того, чтобы значение y было либо 1, либо 0. Например, если у вас есть этот cocd

x=5;
int y = !!x;

Мы знаем, что все, что ненулевые значения в C означают истину. Таким образом, приведенный выше код будет сокращаться до y= !!(5), за которым следует y = !(0), а затем y = 1.

РЕДАКТИРОВАТЬ: Еще одна вещь, я только что видел, как ОП упомянул кастинг на bool. В C нет bool в качестве базового типа, bool является определенным типом, поэтому компиляторы не приводят целые числа к логическому типу.

РЕДАКТИРОВАТЬ 2: Чтобы дополнительно объяснить, в C++, Java и других языках, когда вы вводите bool a = false, вам не нужно использовать заголовки или компилировать некоторые другие библиотеки или определять тип bool для работы типа bool, он уже включен в компиляторы, где, как в с вы должны.

РЕДАКТИРОВАТЬ 3: bool не то же самое, что _Bool.

person KPCT    schedule 06.07.2018
comment
Это может быть правдой, что 1 представляет !false во многих системах, но я почти уверен, что единственное переносимое предположение, которое вы могли сделать (до C99?), заключалось в том, что ноль был ложным, а ненулевое было истинным. Разработчики компилятора могли свободно использовать любой битовый шаблон, который им нравился, в качестве истинного, и должны были принимать любое ненулевое значение как истинное. - person jwdonahue; 07.07.2018
comment
@jwdonahue !false == 1 гарантируется с момента стандартизации по крайней мере (C89), поэтому множество систем включает все системы, соответствующие стандарту. - person eerorika; 07.07.2018
comment
@ user2079303 может быть прав, но я знаю, что gcc и clang гарантируют !0 = 1. Я не могу найти примеры точного кода, но в ядре Linux вы часто видите это if (x == 1), означающее, что x верно. - person KPCT; 07.07.2018
comment
Итак, вы говорите, что int flag = (!0) всегда (начиная с C89) сохраняет значение 1 в flag? - person jwdonahue; 07.07.2018
comment
@jwdonahue Верно. (Пока вы каким-то образом наблюдаете этот факт. Если вы никогда не наблюдаете точное содержимое flag, то оптимизирующий компилятор может сделать что-то хитрое под капотом). Если вы напишете if(!false == 1), то вход в ветку гарантирован. - person eerorika; 07.07.2018
comment
Ну, я знал, что это верно для C99, и вот поток SO, который ссылается на соответствующий стандартный язык. Все еще ищу спецификацию C89... - person jwdonahue; 07.07.2018
comment
@jwdonahue проверьте черновик здесь The result of the logical negation operator ! is ... 1 if the value of its operand compares equal to 0 - person eerorika; 07.07.2018
comment
Ах! Вот оно, на самом деле оно находится по адресу 3.3.3.3 по отношению к оператору логического отрицания. Будучи разработчиком встраиваемых систем с большим стажем, я работал с таким количеством несовместимых компиляторов с начала 80-х, что забыл, как все должно быть на самом деле. - person jwdonahue; 07.07.2018
comment
C язык [...] не имеет bool типа Ну, больше нет. C99 представил _Bool, который является встроенным. - person alk; 07.07.2018
comment
@alk, в отличие от C++, Java и других языков, которые включают тип bool, C не обязательно включать <stdbool.h>, как указывали другие ответы. Таким образом, тип bool не является встроенной функцией C. Просто выполните быстрый поиск по SO, и многие ответы скажут то же самое. Это похоже на автомобильную стереосистему, которая может работать с навигацией, но вы должны добавить (купить) навигационное устройство и подключить его к задней части автомобильной стереосистемы. =( Я не принижаю здесь C язык, а говорю правду. Если вы не добавите заголовок, у вас не будет логического типа - person KPCT; 07.07.2018
comment
@alk посмотрите здесь: stackoverflow.com/questions/1921539/ using-boolean-values-in-c или этот вопрос stackoverflow.com/questions/34683326/boolean-in-c-programming и этот stackoverflow.com/questions/4159713/ .... В C люди просто используют старый добрый int для представления истинного значения. - person KPCT; 07.07.2018
comment
в отличие от C++, Java и других языков, которые включают тип bool, C не требует включения ‹stdbool.h›, это просто неверно. При использовании компилятора C99 или новее этот int main(void) { _Bool b = 1; } компилируется без проблем. Нет включает. Попробуй! - person alk; 11.07.2018
comment
Соответствующую часть стандарта C11 можно прочитать здесь: port70. сеть/~nsz/c/c11/n1570.html#6.7.2 - person alk; 11.07.2018
comment
@alk мой ответ в основном говорит о bool, а не о _Bool и логическом типе в целом. Даже _Bool все еще присутствует не во всех компиляторах. И en _ перед типом означает, что _Bool также является производным типом либо в стандартной библиотеке, либо в компиляторе. Посмотрите на этот список: le.ac.uk/users/rjm1/cotter. /page_19.htm и это: en.cppreference.com/w/cpp /language/types C в отличие от C++ неt include basic Boolean data type. Problem is if you decide not to use C` стандартного компилятора вы получаете int, char, void, float и т. д., но не _Bool, в C++ вы, скорее всего, получаете bool. - person KPCT; 11.07.2018
comment
@Gox: Вы хотя бы просмотрели документы Standard, на которые я ссылался? - person alk; 15.07.2018