3

In resource-constrained systems (like an 8-bit PIC), is there any benefit to using your own macros for true and false as opposed to using those defined from stdbool?

#define TRUE    (1)
#define FALSE   (0)

// OR (dummy example)
#include <stdbool.h>

bool IsThisGood(void)
{
  return true | false;
}

uint8_t IsThisBetter(void)
{
  return TRUE | FALSE;
}

How do they compare in terms of RAM usage? Stack usage? code portability? Code simplicity?

tarabyte
  • 3,112
  • 10
  • 43
  • 67

4 Answers4

4

In resource-constrained systems (like an 8-bit PIC), is there any benefit to using your own macros for true and false as opposed to using those defined from stdbool?

Macros are evaluated by the pre-compiler before compile time, not by the compiler or micro at run time. A macro defined to be "1" will be replaced by "1" before the compiler even starts to try to figure out how to optimize your code. Unless your macro is better than the stdbool macro, there is no benefit.

How do they compare in terms of RAM usage? Stack usage?

1 == 1, regardless of how the macro is defined. No difference.

Code portability?

By using non-standard macros, code portability suffers. Your code will need your defines/headers, so someone can't copy and paste a section that looks standard without getting errors. Especially with something like "True" or "False".

Code simplicity?

If you don't need all of stdbool, sure, defining your own might be simpler. That's really an opinion thing.

Update:

As far as stack usage, I mean to ask what gets returned in each case? 8bits? 16bits? register size of microcontroller?

This is a better question. It's a bit complicated. First, "bool" is really a macro for "_Bool", a c99 data type. Typically, _Bool is the smallest addressable object capable of holding a 0 or a 1. That's a char, an 8 bit sized object. (Things like nibbles are overly complicated objects that have some background code required to split a single 8 bit char into two "separate" 4 bit objects). _Bool also has some logic to where it's 0 when 0, or 1 when anything over or under 0. _bool random_variable = 5 would result in random_variable equaling 1. Memory wise, that bool will be 8 bits, but return literal 0 or 1.

Second, true and false, as you have created macro for, are exactly the same as stdbool.h defines true and false. A literal 0 or 1. Literal intergers are by default, int data types. int is not a fixed standard. It can be defined by your IDE, or compiler, or language. It changes by microcontroller manufacturer or even line of microcontrollers by same manufacturer. It can also be signed or unsigned for the same reasons (See: https://stackoverflow.com/questions/589575/size-of-int-long-etc ) So unless you typecast a variable when assigning true or false, it will be upcasted to int. But uint8_t is a stdint.h unsigned data type of 8 bits as well.

Third, your two functions are not exactly alike. Realize, that IsThisGood returns a bool (really _Bool) type (char/8 bit object). IsThisBetter returns a uint8_t type (8 bits). In practice, the same, but _Bool type has logic to make anything greater than 0 = 1, while uint8_t does not. IsThisBetter could technically return 255 for some reason.

Finally, if for some reason "char" on your microcontroller/ide/compiler/language is greater than 8 bits, then your uint_8 version will be smaller, memory wise, but still lack the "greater than 0 = 1" logic.

See this stack overflow answer on size of _Bool: https://stackoverflow.com/a/10630231/1498667

Passerby
  • 72,580
  • 7
  • 90
  • 202
  • 2
    Being a bit pedantic, if you don't need all of stdbool, the compiler won't compile any of the unused bits in anyway. – John U Mar 14 '14 at 08:59
  • @JohnU sure, but it clogs up the coe with that extra include :P – Passerby Mar 14 '14 at 11:32
  • As far as stack usage, I mean to ask what gets returned in each case? 8bits? 16bits? register size of microcontroller? – tarabyte Mar 14 '14 at 16:00
  • @tarabyte answered and updated. Also, not that these questions are off topic, but stackoverflow has a ton of these type of questions, and while your question is regarding resource limited microcontrollers instead of the typically unconstrained computers, this question would still be expertly answered. – Passerby Mar 15 '14 at 02:25
  • The C standard requires that `sizeof(char)==1`, so while there may be more than 8 bits to a byte on some hardware (very rare), you'll never do better than the built-in bool type without packing multiple booleans into a single byte using bit-logic. Also I believe that stdbool only contains macros, so there is no possibility of unneeded overhead. – Dave Mar 15 '14 at 12:26
  • @Dave are you sure? I thought that required that char be a minimum of 1, with no upper limit. – Passerby Mar 15 '14 at 14:23
  • It comes up a lot, but yes: it's always 1 in c99 and C++. Now if the bits-to-a-byte is more than 8, it gets interesting: the fixed size types are optional, so might not exist, but if they do, they're implemented as bit-shifting. (Suppose you're on a 10-bit system; after your int8_t, everything is misaligned by 2 bits and needs bit-shifting and combining to be usable by the processor. The fixed size types are only intended for consistent communication structs, so speed is a secondary concern). It's all bit-logic in the end (just like giving an exact size in a struct); better to do it yourself. – Dave Mar 15 '14 at 20:59
  • thank you @Dave. To keep explicit how big a bool is, I'm going to use the #define TRUE ((uint8_t)1) method and a return type of uint8_t so that I can return potential initialization errors for some of these predicate functions. – tarabyte Mar 17 '14 at 16:55
3

To answer this question, you have to investigate how your embedded compiler implements the _Bool type, which was introduced in the 1999 revision of the ISO C standard. The _Bool symbol is actually a keyword, like static or int; a keyword beginning with an underscore and capital letter was introduced to avoid clashing with innumerable programs that already define identifiers like bool or BOOL for themselves. When you include the <stdbool.h> header, then you get a bool type which is a synonym for bool and the constants true and false.

If the _Bool type occupies more than one byte, there may be an advantage in using your own type instead.

Although in structures and unions, you can reduce the storage for standard bool fields by making them into bitfield members (for example struct foo { bool x : 1, y : 1; }), you cannot do that in other situations: like local variables or arrays. If bool happens to be two bytes wide, then bool x[32] will be 64 bytes wide, whereas BOOL x[32] can be made just 32 bytes wide, with your own definition of BOOL as a typedef for char. You still retain the option to re-target your BOOL to the C99 bool, in an environment where that makes sense.

If that array is still too big, you will have to make it an array of unsigned char or unsigned int and pack bits with shifting and masking (at the cost of increased code size).

You can experiment and look at generated machine code to see whether code like

x = true; // where x is of bool type

generates the same code, or code that is more or less compact than:

x = TRUE;  // where x is of a type compatible with char, and TRUE expands to 1.

Clearing a byte could easily require more instructions or a bigger instruction on some machine that only deals with words: load a word into memory, set the appropriate part of it to the byte value, then store the whole word, versus just store a zero word into memory. So you might win by saving space in arrays of bools, but lose in code size and cycles. You have to look at generated code and layout of the data.

One more thing: watch out for alignment. Padding due to alignment can negate the space savings you hope for by choice of data type. In a structure, put the members with the strictest alignment requirements first, followed by the less strict members. In this situation, no padding is required, even if the compiler aligns struct members, other than possibly at the end of the structure. That is to say, avoid struct foo { char c1; int a; char c2; int b; }. After each char, there will likely be padding to align the int. If the structure is ordered struct foo { int a, b; char c1, c2; } it will likely be as small as possible on most compilers in existence. At worst, the structure as a whole will have an alignment requirement which is the same as that of its most strictly aligned member (in this case int) which may translate padding at the end, after c2.

Kaz
  • 19,838
  • 1
  • 39
  • 82
2

There will be no difference in RAM usage or stack usage, as the values of TRUE, true, false, and FALSE will all be replaced with the appropriate integers before compilation begins.

The answers to your last two questions would seem to be opinion-based. In my opinion, it is better to use a standard header file when possible. You could argue that this choice makes the code less portable but I do think it improves the simplicity of the code and eliminates any question about whether you have defined TRUE to be different from true for some reason.

Joe Hass
  • 8,447
  • 1
  • 29
  • 41
1

If you include stdbool.h, you get the macro bool which expands to _Bool (C standard keyword), and the macros true and false, which expand to 1 and 0, which are integer literals of type int.

How do they compare in terms of RAM usage? Stack usage?

As already mentioned in another answer, the standard bool type has a better chance to get optimized into a bit field. While a typedef uint8_t BOOL will always be 8 bits. So there is a chance that bool consumes a little less memory, although most likely this optimization won't be done and won't matter to your program.

code portability?

stdbool.h and the _Bool type were introduced in C with the C99 standard. If you are using an old compiler, you may get compatibility issues. I would be particularly wary if using some MPLAB compiler, they are known to have poor standard compliance in general.

Code simplicity?

bool, true and false are to prefer, because they are standard.

BOOL, TRUE and FALSE indicates that the code is old and was written for C90. Or perhaps that the programmer is old and out of date :)

Lundin
  • 17,577
  • 1
  • 24
  • 67