Why the linux kernel uses double logical negations instead of casts to bools?
Asked Answered
P

3

7

Given that x is a variable of type int with the number 5 as its value, consider the following statement:

int y = !!x;

This is what I think it happens: x is implicitly casted to a bool and the first negation is executed, after that the last negation is made, so a cast and two negations.

My question is, isn't just casting to bool (executing int y = (bool)x; instead of int y = !!x) faster than using double negation, as you are saving two negations from executing.

I might be wrong because I see the double negation a lot in the Linux kernel, but I don't understand where my intuition goes wrong, maybe you can help me out.

Patterson answered 6/7, 2018 at 23:10 Comment(3)
There was no bool in C when the Linux kernel was first written.Recha
Bool me twice, shame on me.Fiscal
There is no such thing as “implicitly casted”. A cast is a syntactic construct. x could have been implicitly converted to bool, but that's not what happens in !x, which has type int, and in !!x, which also has type int.Spanos
R
10

There was no bool type when Linux was first written. The C language treated everything that was not zero as true in Boolean expressions. So 7, -2 and 0xFF are all "true". No bool type to cast to. The double negation trick ensures the result is either zero or whatever bit pattern the compiler writers chose to represent true in Boolean expressions. When you're debugging code and looking at memory and register values, it's easier to recognize true values when they all have the same bit patterns.

Addendum: According the C89 draft standard, section 3.3.3.3:

The result of the logical negation operator ! is 0 if the value of its operand compares unequal to 0, 1 if the value of its operand compares equal to 0. The result has type int . The expression !E is equivalent to (0==E).

So while there was no Boolean type in the early days of the Linux OS, the double negation would have yielded either a 0 or a 1 (thanks to Gox for pointing this out), depending on the truthiness of the expression. In other words any bit pattern in the range of INT_MIN..-1 and 1..INT_MAX would have yielded a 1 and the zero bit pattern is self-explanatory.

Recha answered 6/7, 2018 at 23:32 Comment(1)
@Ivan, thanks for the edit, my lesdixya must have kicked in, because I clearly remember looking at the <stdint.h> file before writing those constants! ;(Recha
R
1

C language unlike other languages does not have bool type. bool in C is actually defined in stdbool.h which is not included in many C projects. Linux kernel is one such projects, it would be a pain to go through Linux code and update everything to use bool now as well. That is reason why Linux kernel does not use bool in C.

why !!x? This is done to ensure that value of y is either 1 or 0. As an example if you have this cocd

x=5;
int y = !!x;

We know that everything that non-zero values in C mean true. So above code would brake down to y= !!(5) followed by y = !(0) and than y = 1.

EDIT: One more thing, I just saw OP mentioned casting to bool. In C there is no bool as base type, bool is defined type, thus compilers do not cast integers to Boolean type.

EDIT 2: To further explain, in C++, Java and other languages when you type bool a = false you do not have to use headers or compile some other libraries or define bool type for bool type to work, it is already incorporated into compilers, where as in c you have to.

EDIT 3: bool is not the same as _Bool.

Radbourne answered 6/7, 2018 at 23:35 Comment(15)
It may be true that 1 represents !false on many systems, but I am pretty sure that the only portable assumption you could make (prior to C99?), was that zero was false and non-zero was true. The compiler writers were free to use whatever bit pattern they liked for true and had to accept any non-zero value as true.Recha
@Recha !false == 1 has been guaranteed since standardization at least (C89), so "many systems" includes all standard conforming ones.Phane
@user2079303 could be right, but all I know that gcc and clang guaranty !0 = 1. I cannot find examples of exact code, but in Linux kernel you would often see this if (x == 1) meaning x is true.Radbourne
Ok, so you're saying that int flag = (!0) always (since C89) stores the value of 1 in flag?Recha
@Recha That's right. (As long as you observe that fact somehow. If you never observe the exact contents of flag, then an optimizing compiler may do something tricky under the hood). If you write if(!false == 1), the branch is guaranteed to be entered.Phane
Well I knew that was true for C99 and here's an SO thread that references the relevant standardese. Still looking for the C89 spec...Recha
@Recha check the draft here The result of the logical negation operator ! is ... 1 if the value of its operand compares equal to 0Phane
Ah! There it is, it's actually at 3.3.3.3, in reference to the logical negation operator. Being a long-time embedded systems developer, I've worked with so many non-compliant compilers since the early 80's, I forget how things actually should be.Recha
"C language [...] does not have bool type" Well, not anymore. C99 introduced _Bool, which is intrinsic.Suribachi
@Suribachi unlike C++, Java, and other languages which include bool type, C does not you have to include <stdbool.h> as other ansers have pointed out. Thus bool type is not built in feature of C. Just do a quick search on SO and buch of answers would say the same. It is kind of like car stereo that can handle navigation but you have to add (buy) navigation device and plug it into back of your car stereo. =( I am not downing C language here, but actually telling the truth. Unless you add the header you have no Boolean typeRadbourne
@Suribachi look here: https://mcmap.net/q/53575/-using-boolean-values-in-c or this question https://mcmap.net/q/1480188/-boolean-in-c-programming and this #4160213 .... In C people just use plain old int to represent true value.Radbourne
"unlike C++, Java, and other languages which include bool type, C does not you have to include <stdbool.h>" this simply is not true. Using a C99 or newer compiler this int main(void) { _Bool b = 1; } compiles without any issues. No includes. Try it!Suribachi
The relevant part of the C11 standard can be read up here: port70.net/~nsz/c/c11/n1570.html#6.7.2Suribachi
@Suribachi my answer mostly talks about bool not about _Bool and Boolean type in general. Even _Bool is still not present in all compilers. And en _ before type means _Bool is derived type as well in either standard library or compiler. Look at this list: le.ac.uk/users/rjm1/cotter/page_19.htm and this: en.cppreference.com/w/cpp/language/types C unlike C++ doesnt include basic Boolean data type. Problem is if you decide not to use C` standard compiler you are getting int, char, void, float etc but not _Bool, in C++ you are most likely getting bool.Radbourne
@Gox: Did you even took a look at the Standard documents I linked?Suribachi
E
0

The only reason I can imagine is because this saves some typing (7 chars vs 2 chars).

As @jwdonahue and @Gox have already mentioned, this is not the correct reason. C did not have bool when the linux kernel was written therefore casting to bool was not an option.

As far as efficiency goes, both are equivalent because compilers can easily figure this out. See https://godbolt.org/g/ySo6K1

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

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

Both the functions compile to the same assembly which uses the test instruction to check if the argument is zero or not.

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
Esculent answered 6/7, 2018 at 23:15 Comment(1)
It's only required to use 7 chars if you are using _Bool instead of bool. But, bool requires #include <stdbool.h>.Fiscal

© 2022 - 2024 — McMap. All rights reserved.