Go Down

Topic: Use of !! in C language (Read 450 times) previous topic - next topic

bperrybap

#15
Aug 31, 2015, 12:51 am Last Edit: Aug 31, 2015, 12:52 am by bperrybap
Another alternative
Code: [Select]

digitalWrite( dataPin, (bool)(val & (1 << i)) );

That is not the same thing as using HIGH and LOW.

This example as well as all the others that use booleans abuse the digitalWrite() API interface.
The digitalWrite() interface is defined to take HIGH and LOW as a value parameter.
The fact that a particular digitalWrite() code implementation may work when using a non zero value instead of HIGH is not a valid reason to use it since using a non zero value to get a high level output is not part of the digitalWrite() API.
https://www.arduino.cc/en/Reference/DigitalWrite

It is possible that it may not work in all digitalWrite() implementations.

The proper way to use an API function is to use it the way the API has documented and defined it.
Once you step outside using an API the way it is the defined, you open your code up to potential future failures should the internal coding implementation change.

Therefore, the proper way to do it is as  oqibidipo described:

Code: [Select]
digitalWrite(dataPin, (val & (1 << i)) ? HIGH : LOW );

all the other examples that are using booleans are abusing the API and not using digitalWrite() as it is documented.

---

My only guess as to why digitalWrite() was not originally defined to take a boolean is that it generates poor code on the AVR than using a uint8_t (or at least it used to) That is because booleans were essentially ints which are 16 bits on the AVR and the AVR doesn't handle 16 bit values efficiently as 8 bit values.

BTW,
As a side note, it would be within the existing digitalWrite() API definition if digitalWrite() extended to support PWM
by changing HIGH to be defined as 255 and LOW as 0 and then LOW would be low output and HIGH would be a high output and any value between LOW and HIGH were a PWM output.
If that were done, all those applications that followed the digitalWrite() API properly and used HIGH and LOW would continue to work properly, but all those that abused the API by using non zero values instead of HIGH would have their code break.

--- bill

zoomkat

I think in the past it has been found putting !!! in code like below may cause problems.

Code: [Select]
Serial.println("I found it, woohoo!!!");
Google forum search: Use Google Search box in upper right side of this page.
Why I like my 2005 Rio Yellow Honda S2000  https://www.youtube.com/watch?v=pWjMvrkUqX0

GoForSmoke

#17
Aug 31, 2015, 04:32 am Last Edit: Aug 31, 2015, 04:35 am by GoForSmoke
What's wrong with

Code: [Select]
byte makeBool x = (( val != 0 ) ? HIGH : LOW );

?

Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

bperrybap

#18
Aug 31, 2015, 07:03 am Last Edit: Aug 31, 2015, 07:09 am by bperrybap
What's wrong with
Code: [Select]
byte makeBool x = (( val != 0 ) ? HIGH : LOW );
makeBool ?? as a "standalone" keyword? What is that? That line doesn't look like valid C/C++ syntax to me.



The key thing to keep in mind that the actual values of HIGH and LOW cannot be assumed to be any particular value.
To assume so violates the API interface definition.
i.e. the API implementation should be free to assign any value it wants to the symbols HIGH and LOW and it should not break any code that properly uses the API.

wired67

Hi,

the !! is used as a safer way to cast (as opposed to a normal cast) a number to a boolean in c++, because of how c++ treats bools.

In c++, false has a value of 0, and true is any other number.  however the 'true' keyword has a value of 1.

Take the following code as an example:

Code: [Select]


int number = 7;
bool bValue = (bool)number;
bool cValue = bValue;

if(bValue == true)
{
   print("bValue is true");
}
else
{
   print("bValue is false");
}

if(cValue)
{
   print("cValue is true");
}
else
{
   print("cValue is false");
}



The output of this code will not be what you expect. You will get the following output:

bValue is false
cValue is true

The reason for this is when you do a direct cast, the memory where the bool is stored will still contain the binary for teh number seven, so the line

if(bValue == true)

is actually doing

if(7 == 1)

which is obviously false.

The other line works, because its not checking against a specific value, so its true.

So to answer the actual question, what the !! does is that it will first convert the number to false (so the memory will be 0) and will then immediately switch it back to true (so it will have a value of 1).

Try running the above code through a debugger and casting bValue back to an int to see this in action.

I hope that all makes sense.

Coding Badly

Quote
The output of this code will not be what you expect.
The output is what I expect.  But not what you expect.

@wired67, I suggest you actually run the test.


GoForSmoke

makeBool ?? as a "standalone" keyword? What is that? That line doesn't look like valid C/C++ syntax to me.



The key thing to keep in mind that the actual values of HIGH and LOW cannot be assumed to be any particular value.
To assume so violates the API interface definition.
i.e. the API implementation should be free to assign any value it wants to the symbols HIGH and LOW and it should not break any code that properly uses the API.
The name is to make an example clear, pick another like x or n if you want.

No I can't assume anything about HIGH or LOW except that they should work the same as HIGH or LOW when I type it into source no matter what compiler. The same so what's the problem?

I put up an example for Arduino IDE on an Arduino forum. I expect that the expression is valid in most other C, excepting where the name makeBool is already taken then change the name.
Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

RayLivingston

What's wrong with

Code: [Select]
byte makeBool x = (( val != 0 ) ? HIGH : LOW );

?


What, exactly, is it you're trying to do?  The "byte" says you are declaring a byte variable.  The "makeBool" then would be the name of that variable.  So what is the "x" doing there?  Or, perhaps you meant "x" to be the name of the variable, in which case what is the "makeBool" doing there?

Regards,
Ray L.

GoForSmoke

Now I see. Yeah that x does not belong. My BAD.
Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

christop

The reason for this is when you do a direct cast, the memory where the bool is stored will still contain the binary for teh number seven, so the line
Not so. When you cast an integer to bool, C and C++ will convert 0 to false and any non-zero value to true. So bValue and cValue will both be true.

bperrybap

The reason for this is when you do a direct cast, the memory where the bool is stored will still contain the binary for teh number

The only way that can happen is if the cast is not for a type bool.

If, for example, you used the proprietary Arduino type boolean instead of bool there is the possibility that the cast will not create a real boolean true/false value and will instead return the original binary value.
This is because in every single version of Arduino until version 1.6 boolean was typedefed as a uint8_t and not a bool.
Starting in Arduino 1.6 boolean was changed to be bool.
On the AVR a uint8_t will generate better code and use less RAM than a bool (may be this has changed on the newest AVR compilers? I haven't looked in a year or so).
Back when I looked, a bool was essentially a type int and so it used two bytes and the compiler compared both bytes when checking against true/false so a uint8_t was more efficient when using it as a "bool" rather than actually declaring it as a bool.


Go Up