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.

Garmekain
  • 664
  • 5
  • 18
  • 5
    There was no bool in C when the Linux kernel was first written. – jwdonahue Jul 06 '18 at 23:22
  • 2
    *Bool me twice, shame on me.* – jxh Jul 06 '18 at 23:32
  • 2
    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`. – Pascal Cuoq Jul 07 '18 at 00:49

3 Answers3

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.

jwdonahue
  • 6,199
  • 2
  • 21
  • 43
  • @Ivan, thanks for the edit, my lesdixya must have kicked in, because I clearly remember looking at the file before writing those constants! ;( – jwdonahue Jul 14 '18 at 04:39
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.

  • 1
    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. – jwdonahue Jul 06 '18 at 23:43
  • 3
    @jwdonahue `!false == 1` has been guaranteed since standardization at least (C89), so *"many systems"* includes all standard conforming ones. – eerorika Jul 06 '18 at 23:51
  • @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. –  Jul 06 '18 at 23:55
  • Ok, so you're saying that `int flag = (!0)` always (since C89) stores the value of `1` in `flag`? – jwdonahue Jul 06 '18 at 23:55
  • @jwdonahue 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. – eerorika Jul 06 '18 at 23:59
  • Well I knew that was true for C99 and [here's an SO thread that references the relevant standardese](https://stackoverflow.com/questions/39730583/return-value-of-a-boolean-expression-in-c). Still looking for the C89 spec... – jwdonahue Jul 07 '18 at 00:01
  • @jwdonahue check the draft [here](https://port70.net/~nsz/c/c89/c89-draft.html#3.1.5) `The result of the logical negation operator ! is ... 1 if the value of its operand compares equal to 0` – eerorika Jul 07 '18 at 00:04
  • Ah! There it is, it's actually at [3.3.3.3](https://port70.net/~nsz/c/c89/c89-draft.html#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. – jwdonahue Jul 07 '18 at 00:32
  • 1
    "*`C` language [...] does not have `bool` type*" Well, not anymore. C99 introduced `_Bool`, which *is* intrinsic. – alk Jul 07 '18 at 06:20
  • @alk unlike `C++`, `Java`, and other languages which include `bool` type, `C` does not you have to include `` 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 type –  Jul 07 '18 at 18:18
  • @alk look here: https://stackoverflow.com/questions/1921539/using-boolean-values-in-c or this question https://stackoverflow.com/questions/34683326/boolean-in-c-programming and this https://stackoverflow.com/questions/4159713/how-to-use-boolean-datatype-in-c .... In `C` people just use plain old `int` to represent true value. –  Jul 07 '18 at 18:22
  • "*unlike C++, Java, and other languages which include bool type, C does not you have to include *" 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! – alk Jul 11 '18 at 14:02
  • 2
    The relevant part of the C11 standard can be read up here: http://port70.net/~nsz/c/c11/n1570.html#6.7.2 – alk Jul 11 '18 at 14:22
  • @alk 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: https://www.le.ac.uk/users/rjm1/cotter/page_19.htm and this: https://en.cppreference.com/w/cpp/language/types `C` unlike `C++` doesn`t 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`. –  Jul 11 '18 at 17:34
  • @Gox: Did you even took a look at the ***Standard*** documents I linked? – alk Jul 15 '18 at 13:10
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
lakshayg
  • 2,053
  • 2
  • 20
  • 34
  • 2
    It's only required to use 7 chars if you are using `_Bool` instead of `bool`. But, `bool` requires `#include `. – jxh Jul 06 '18 at 23:26