[gcc C] I have been blindly doing this but how does it work  - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: October 09, 2018, 08:24:57 am
content: #define BIT_GET(p,m) ((p) & (BIT(m)))#define BIT(x) (0x01 << (x))So I get the bit masking stuff but but how is some random byte that is the result is understood as a boolean state.So 01010101 & 00000100 = 00000100How does 00000100 equate the Boolean state of 1 ?

author: Psi
author_rank: Super Contributor
timestamp: October 09, 2018, 08:28:34 am
content: All numbers are interpreted as boolean 1, except zero which is boolean 0eg, these will loop forever because 4 is evaluated as true (1)while (4){}while (0b00000100){}

author: Simon
author_rank: Unknown
timestamp: October 09, 2018, 08:33:50 am
content: Right, perfect, thank you.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 09, 2018, 07:23:41 pm
content: Yes - what you did is thus perfectly legit.There is no boolean type per se in C. Any integer different from 0 is considered true in any conditional statement, and any integer equal to 0 is false.I often write conditions such as "xxx & MASK". They are compact and readable.A corollary (and pitfall for people used to C++ only for instance) of this is that you should never assume any C expression used as a condition to be exactly 1 when true, or any other magic value. It can be any value other than zero.One common way of "normalizing" a boolean expression in C is to use a double logic negation: if you write "!!(xxx & MASK)" you should get exactly either 0 or 1. I've seen this in various source codes. Note that it's absolutely not required, as manipulating the result value of a condition makes no sense to me. Some people may be tempted to use this in an attempt to optimize expressions (for instance multiplying a "boolean" value within some expression so as to avoid using an explicit test), but I consider this obfuscating, and decent modern compilers will make optimizations that are much better than those hand-written tricks anyway.

author: glarsson
author_rank: Frequent Contributor
timestamp: October 09, 2018, 07:38:03 pm
content: Quote from: SiliconWizard on October 09, 2018, 07:23:41 pmSince 1999 you can include stdbool.h to enable the bool type. This type can only have the value of 0 or 1.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 09, 2018, 08:01:53 pm
content: Yes, but this is essentially sugar coating. Conditional expressions can still be plain integers. And I've just re-checked the standard: there's nothing there that I've seen that actually guarantees by standard that a 'bool' variable will hold only 0 or 1 either. It may or may not depending on the compiler's implementation as far as I've understood it. The header just declares a macro 'bool' that expands to '_Bool'.The standard only states that "The type_Bool and the unsigned integer types that correspond to the standard signed integer types are the standard unsigned integer types." and that "An object declared as type _Bool is large enough to store the values 0 and 1." Large enough doesn't mean "only". Nothing prevents the compiler from just using an unsigned integer as _Bool (as is suggested in the other sentence). Just states that it should be at least 1 bit wideHaving tested it on GCC, GCC actually seems to reduce values to 0 or 1 on bool assignments, but I don't think it's required. I'm taking this from the C 2011 std though, so that may have evolved a bit.I've actually been using this type for years now, but essentially to convey meaning and improve readability. It doesn't change squat to the behavior as far as I'm concerned. And again, expecting a specific integer value for a bool would be neither wise nor readable IMHO.

author: Simon
author_rank: Unknown
timestamp: October 09, 2018, 08:06:59 pm
content: i don't bother with bool's as it's an 8 bit CPU anyway so nothing gained.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 09, 2018, 08:29:37 pm
content: Quote from: Simon on October 09, 2018, 08:06:59 pmI'd still suggest using the bool type for boolean value storage as this has become part of the standard and will be easier to read than using the pervasive "int" instead that we used to use. But other than that, it doesn't change much. I find it more readable though. But again, I strongly advise against assuming that it will get any particular value.In the same vein, I strongly suggest using the stdint.h header and the standard integer types in it, especially in embedded software. They have added a much-needed standard way of declaring variables with exact bit width and signedness (such as 'uint8_t', etc), whereas before that we used all kinds of tricks for that that were largely non-portable.Concerning the macros that you submitted, that doesn't change anything at all. They are still valid and no mention of the bool type is necessary. So the bool type is mainly for variable/struct members declaration.

author: newbrain
author_rank: Super Contributor
timestamp: October 10, 2018, 06:58:31 am
content: Quote from: SiliconWizard on October 09, 2018, 08:01:53 pm6.3.1.2 would seem pretty clear to me.QuoteBut of course:Code:bool b;*(uint8_t *)&b = 123;I would saythat's UB, though(I have to check).EtA: I have checked, 6.5 clause 7, first bullet.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 10, 2018, 07:08:50 am
content: Quote from: SiliconWizard on October 09, 2018, 08:29:37 pm... or at least don't use "int" on 8-bitters unless you are sure about what you are doing - it's likely to be reproduced as 16-bit integer, as the C standard requires IIRC, with the obvious overhead of software emulation of 16-bit arithmetic, and memory consumption. If it's a volatile used to pass information between the ISR and the main loop, the compiler doesn't even have a chance to optimize it. And then if you make the mistake to expect it's atomic, like the 8-bit bool would be...QuoteYes, 110% this. Even if you don't end up using bool, you need standard readable types of integers. The options are, define your own (like we did before the standard existed) in your own header, or use the standard way of stdint or inttypes.

author: JPortici
author_rank: Super Contributor
timestamp: October 10, 2018, 07:32:15 am
content: Quote from: Simon on October 09, 2018, 08:06:59 pmyou gain in readability. I am gradually updating all the old firmwares to remove every bit of assembly that don't compromize functionality, every implicit bitfield converted to a bitfield structure and so on.

author: tombi
author_rank: Regular Contributor
timestamp: October 10, 2018, 02:57:03 pm
content: Actually some compilers (like the Microsoft dinosaur I use for work) will give you a ‘performance warning’ if you assign an int to a bool. I think the compiler uses some tricks to make manipulating bool types faster.So I also agree - use the bool type for booleans and not just because it make the code make sense.TomSent from my iPhone using Tapatalk

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 10, 2018, 03:24:35 pm
content: Quote from: newbrain on October 10, 2018, 06:58:31 amTrue. The key is that it needs a conversion to do this. The underlying storage itself can be as wide as the compiler wants, making it thus possible that a "bool" declared variable would hold something else than 1 or 0 in some cases.My point is that IMO, it's bad practice to assume only 0 or 1 for a conditional expression in C in general, and integer expressions that are not of type 'bool' are still valid in C as conditions. Thus I happen to actually not care and not *want* to care. I still think this is some kind of sugar coating as C doesn't strictly *require* a boolean-type expression for conditions, so it's only half-integrated in the C language IMO (obviously not to break existing code, so it makes perfect sense). Now if it was required as in very strongly-typed languages such as ADA, that would be another story.My biggest pet peave with "boolean" expressions in C is as I mentioned the "trick" expressions that could be encouraged by this and that are hard to read, would break in non-compliant compilers and should be avoided at all costs IMO (optimizers being much more efficient for this anyway):Code:bool b = W & MASK;int n = b*10 + 1;

author: JPortici
author_rank: Super Contributor
timestamp: October 10, 2018, 03:29:10 pm
content: Quote from: SiliconWizard on October 10, 2018, 03:24:35 pmstdbool.h in XC8 V2.00 has#define false 0#define true !falsei havent' checked in other compilers but i don't expect to see anything different

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 10, 2018, 03:46:27 pm
content: Small example:Code:#include <stdio.h>#include <inttypes.h>#include <stdbool.h>int main(void){bool b1;union { bool b; int n; } b2;b1 = 25;printf("b1 = %d\n", b1);printf("sizeof b1 = %"PRIu64"\n", sizeof b1);b2.n = 10;printf("b2.b = %d\n", b2.b);}On an x64 target with GCC gives:Code:b1 = 1sizeof b1 = 1b2.b = 10Implicit conversions are only done while assigning bool variables.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 10, 2018, 04:01:09 pm
content: Quote from: JPortici on October 10, 2018, 03:29:10 pmNot sure you got the main point. First because those are just constants that are guaranteed to be seen as either false or true in logic/conditional expressions, nothing more. Second because those macros are valid even for very old versions of C. This is exactly how TRUE and FALSE constants have been declared in lots of various C headers for decades. Obviously those declarations are correct: they are just tautologies. Not false is true, not true is false. And 0 is false in C. What else does it actually say?Again there is no bijection in C between the "true" logic state and the value "1". There only is for the "0" value.There kind of is a bijection if you strictly stay in the realm of 'bool'-typed expressions with C >= 99, with pitfalls. And again, I personally don't care and think this is something that should have been avoided in the C standard, for reasons given above and mostly because it leaves an inconsistency (again since any value other than 0 in C is still considered true, even pointers). A true boolean type integrated in C should have prevented IMO any implicit conversion from any other type (and only allow expressions with bool values and logic operators). Just my opinion of course. Again they have obviously chosen a compromise not to break existing code, thus the half-baked solution.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 10, 2018, 04:18:10 pm
content: Quote from: tombi on October 10, 2018, 02:57:03 pmIf the compiler in question supports the bool type and C99, it's not completely a dinosaur. Microsoft's older compilers are known not to support C99 very well though, or only partially.In your example, the warning makes perfect sense and I think you considered it backwards. As newbrain pointed out and I discussed afterwards (and showed an example), assigning an integer to a bool will do an implicit conversion and thus will "clip" the integer value to one if it's different from zero. This clipping operation costs more than a simple assignment. Thus the performance warning.Due to this, depending on the compiler and the assignments/expressions that are used, using the bool type may thus actually be less efficient than keeping your logic expressions as integers.Again, it mostly adds to readability, prevents (or should) developers from declaring their own boolean types all over the place, and may have been a decision to sort of bridge a gap between C and C++ concerning boolean values, but not much more. Any decent optimizer will optimize your logic expressions probably just as well if you don't use the bool type. As seen above, most compilers (GCC does at least) will store 'bool' variables as 8-bit integers (although it's not strictly required, but makes sense) though, and thus they will take less storage space than an 'int' on most targets, so that can be a plus.

author: glarsson
author_rank: Frequent Contributor
timestamp: October 10, 2018, 05:32:03 pm
content: Quote from: SiliconWizard on October 10, 2018, 03:46:27 pmStoring something in a union member and retrieving it from a different member invokes undefined behavior. Anything can happen, so I don't know what this example was supposed to prove.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 10, 2018, 06:12:24 pm
content: Quote from: glarsson on October 10, 2018, 05:32:03 pmYeah, per standard. And standard is just paper.Yes, demons may fly out of your nose. In theory.In reality, unions are usedexactlyfor this purpose so widely, that every attempt to make a compiler break the existing software gets immediate backlash. This has been the case for decades - especially in embedded.Union is simply really useful for this purpose.More discussion here:https://stackoverflow.com/questions/252552/why-do-we-need-c-unions- the best accepted answer agrees with my viewpoint.But I guess this is a so-called "opinion" thing and I respect anyone's idea of adhering to the standard to the letter.

author: glarsson
author_rank: Frequent Contributor
timestamp: October 10, 2018, 06:19:48 pm
content: What happens down the line with this bool with a value of 10? The compiler is free to optimize the use of the bool and might generate code that only looks at bit 0.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 10, 2018, 06:23:56 pm
content: Yes in practice this has been so widely used that no compiler that I've ever run into would try and map union fields on different offsets. So this is basically equivalent to using pointers. If you're going to get stuck up about it, you may even say that I made assumptions as to the endianness. The exact result doesn't matter anyway.Although it didn't really even need to be proven, it just shows that a bool declared variable may contain many other values than 0 and 1 and that again, the bool type in C is not much more than sugar coating. For the rest of my point read above.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 10, 2018, 06:33:54 pm
content: Quote from: glarsson on October 10, 2018, 06:19:48 pmFor constant values, the "optimization" will occur at compile time anyway, not at run time.The compiler is free to do whatever it wants if you only use bool's in conditions (same as with plain int expressions). If you use bool's values in arithmetic expression such as the example I gave, it's not. It will coerce it into 0 or 1. Again I find this madness to do so anyway.As for the above example of 10, I'm afraid looking at bit 0 won't tell you much as 10 is "true" in the C logic sense yet its bit 0 is 0. But that's just a detail. Point is, yes optimizers will still do whatever they want/can to simplify expressions in ways that may be far away from what you would by hand.

author: glarsson
author_rank: Frequent Contributor
timestamp: October 10, 2018, 06:38:15 pm
content: Quote from: SiliconWizard on October 10, 2018, 06:23:56 pmI didn't argue that something odd would happen inside the union. I fully expect the bool to "have" the value 10 in the example. What I am arguing is that the compiler is free to assume the bool will never have a value other than 0 and 1. Going behind the back of the compiler to set bits ad hoc can lead to problems, both for bool, pointers and other data types.

author: JPortici
author_rank: Super Contributor
timestamp: October 10, 2018, 08:38:17 pm
content: Quote from: SiliconWizard on October 10, 2018, 04:01:09 pmof course i got it, i was just

author: langwadt
author_rank: Super Contributor
timestamp: October 10, 2018, 09:36:37 pm
content: Quote from: Simon on October 09, 2018, 08:06:59 pmI don't see why it couldn't gain something on some platforms where the compiler could use bits to store bools and bitwise instructions to manipulate them

author: westfw
author_rank: Super Contributor
timestamp: October 10, 2018, 11:24:15 pm
content: ARM missed the chance to add true single-bit variables as a extension to their compilers, in conjunction with the "bit-banding" hardware feature.It could've worked really nicely - pointers to single bits would have worked fine, and everything.But it would have been non-standards-conforming, and seemed to mostly be aimed at IO ports that were better handled via other mechanisms, so they left it for users to do on their own, and so it mostly "didn't happen." And now most new ARMs don't even implement bit-banding, so the the opportunity is gone...

author: langwadt
author_rank: Super Contributor
timestamp: October 10, 2018, 11:37:58 pm
content: Quote from: westfw on October 10, 2018, 11:24:15 pmafaiu bit banding was dropped because it doesn't play well with data caches

author: tombi
author_rank: Regular Contributor
timestamp: October 10, 2018, 11:52:13 pm
content: Quote from: SiliconWizard on October 10, 2018, 04:18:10 pm

author: brucehoult
author_rank: Super Contributor
timestamp: October 11, 2018, 03:46:08 am
content: Quote from: Simon on October 09, 2018, 08:24:57 amIf you want to get a result of only 00000000 or 00000001 then use this instead:Code:#define BIT_GET(p,m) (((p)>>(m)) & 1)That's a bit more work for the CPU as the shift is done at run time instead of compile time. If you've got a proper multi-bit shifter then it's likely to be only one extra instruction/cycle and so not to be worried about.

author: westfw
author_rank: Super Contributor
timestamp: October 11, 2018, 04:41:06 am
content: QuoteIf m is a constant, the compiler will optimize weirdly for you...Code:bool get_b6() {return BIT_GET(PORTB, 6);0: 85 b1 in r24, 0x05 ;; read portb2: 86 fb bst r24, 6 ;; set T status bit from bit 64: 88 27 eor r24, r24 ;; zero return value6: 80 f9 bld r24, 0 ;; load bit 0 (true or false) from T8: 08 95 retI get versions using a shift & and (b1), just an and (b0), and the carry bit (b7)QuoteAlas, no "proper" shifter on any of the AVR chips. going to a variable bit number gets you a loop. Computed with a 16bit int, too. Sad.Code:bool get_bv(uint8_t b) {return BIT_GET(PORTB,b);2c: 25 b1 in r18, 0x05 ; 52e: 30 e0 ldi r19, 0x00 ; 030: a9 01 movw r20, r1832: 00 c0 rjmp 3834: 55 95 asr r2136: 47 95 ror r2038: 8a 95 dec r243a: 02 f4 brpl 343c: ca 01 movw r24, r203e: 81 70 andi r24, 0x01 ; 140: 08 95 ret

author: tombi
author_rank: Regular Contributor
timestamp: October 11, 2018, 05:01:43 am
content: Why notCode:#define BIT_GET(p,m) ( (p) & (BIT(m)) != 0 )#define BIT(x) (0x01 << (x))

author: westfw
author_rank: Super Contributor
timestamp: October 11, 2018, 05:47:30 am
content: Needs another set of parens, after which it produces exactly the same code (for AVR) as the previous macro.

author: tombi
author_rank: Regular Contributor
timestamp: October 11, 2018, 05:55:22 am
content: Ok yes brackets...Reasonably surprised that the generated code is the same as the previous definition - #define BIT_GET(p,m) (((p)>>(m)) & 1).The BIT() macro should evaluate to a compiler constant.Then BIT_GET(p,m) assuming M is a constant evaluates to ((p & constant) != 0)I only suggest adding the !=0 as then the result is a boolean and not a number.So I assume it is used likeif ( BIT_GET(something,BIT(3)) ){...

author: westfw
author_rank: Super Contributor
timestamp: October 11, 2018, 06:42:09 am
content: yep. As you say. The macros evaluate to the same code on AVR, CM0, and CM4. (all gcc, so that's not really surprising.)CM4 ends up using the "bit field extract" instruction, which is pretty nice.here's code that should be pretty platform-independent:Code:#include <stdint.h>volatile uint8_t PORTB;#define BIT_GET(p,m) ( ((p) & (BIT(m))) != 0 )#define BIT(x) (0x01 << (x))#define BIT_GET1(p,m) ((((uint8_t)p)>>((uint8_t)m)) & (uint8_t)1)bool get_b3() {return BIT_GET(PORTB, 3);}bool get1_b3() {return BIT_GET1(PORTB, 3);}bool get_bv(uint8_t b) {return BIT_GET(PORTB,b);}bool get1_bv(uint8_t b) {return BIT_GET1(PORTB,b);}int main() {}extern "C" { void _exit(int c) {while(1);} }

author: cv007
author_rank: Frequent Contributor
timestamp: October 11, 2018, 09:32:51 am
content: Code:#include <stdint.h>#include <stdbool.h>static inline bool bit_get(volatile uint8_t* p, uint8_t b){return *p & (1<<b) ? 1 : 0;}static inline void bit_set(volatile uint8_t* p, uint8_t b, bool tf){if(tf) *p |= (1<<b); else *p &= ~(1<<b);}void main(){//just guessing that 0x24 will get me porta, 0x25 portb (IN/OUT instructions)//PORTA.3 = PORTB.6bit_set((volatile uint8_t*)0x24, 3, bit_get((volatile uint8_t*)0x25,6));for(;;);}/*Disassembly of section .text:00000000 <main>:0: 2e 9b sbis 0x05, 6 ; 52: 02 c0 rjmp .+4 ; 0x8 <__zero_reg__+0x7>4: 23 9a sbi 0x04, 3 ; 46: 01 c0 rjmp .+2 ; 0xa <__zero_reg__+0x9>8: 23 98 cbi 0x04, 3 ; 4a: ff cf rjmp .-2 ; 0xa <__zero_reg__+0x9>*/

author: westfw
author_rank: Super Contributor
timestamp: October 11, 2018, 09:50:45 am
content: Yeah; except your use of inlines, and compiler optimization, means that you never actually have a "bool."I figure I'm a bit upset that I don't get:Code:ldi r24, 1 ;; assume true sbis PORTB,6 ;; test IO bitxor r24, r24 ;; we were wrong; set false. ret

author: cv007
author_rank: Frequent Contributor
timestamp: October 11, 2018, 11:48:31 am
content: Quoteexact same result as the macro

author: ahbushnell
author_rank: Frequent Contributor
timestamp: October 13, 2018, 07:18:13 pm
content: Quote from: Simon on October 09, 2018, 08:24:57 amIts bit wise. So each bit is operated on by its position.Sent from my SM-G930V using Tapatalk