Writing software that is reusable, portable and will stand the test of time is critical to embedded software developers. Below are 7 tips for creating and using portable types in C.
Tip #1 – Use stdint.h
The use of built-in data types can result in portability issues between different compilers. The C standard does not specify the storage size of an integer which results in some compiler vendors using 2 or 4 bytes to store an integer. The result can potentially be disastrous.
A safe alternative to using the built in data types is to instead use the stdint.h fixed width integers. These data types, uint8_t, int8_t, uint16_t, int16_t, uint32_t and int32_t specify the number of storage bits required to store the data. Porting any of these data types results in the same behavior in any compiler and won’t result in four bytes of storage suddenly becoming two.
Tip#2 – Use stdbool.h
Developers at times may decide that they would like a data type that can store a simple true or false value. The standard boolean data type and its true or false definitions can be brought into a project by including stdbool.h.
Tip #3 – Don’t custom define TRUE, FALSE, bool
In the previous tip, it is recommended to use stdbool.h which is a standard library file. Some developers may not realize that stdbool exists or may decide not to use it. Instead, they custom define bool, true and false. The problem with this is that when porting code with customized versions, a mismatch can occur where true, false and bool are attempted to be redefined and the compiler complains. Don’t create your own version just simply follow Tip #2.
Tip #4 Shy away from custom integer types
Using typedef to create concise and clear type names is a great way to write embedded software. The problem arises when developers start to define non-standard integer types that look very close to standard ones. For example, take the following custom type
typedef volatile uint16_t vuint16_t;
Seeing vuint16_t in software would be confusing. What is the v? Obfuscating the volatile keyword could be dangerous! Shouldn’t a developer be forced to write volatile so that a developer reading the code knows that the variable is volatile? Reading vuint16_t could very easily be misread as uint16_t or worse, a developer could accidentally type uint16_t and completely miss the v.
Keep to standard types and make the use of important keywords like volatile explicit and directly in the face of anyone reading or maintaining the code.
Tip #5 – Custom types should have _t
When defining a custom type use _t to match the fixed width integer types. It is simple and concise. Take for example the following;
It becomes quite obvious that State_t is a typedef.
Tip #6 – Use enumerated custom types
Creating custom data types similar to that shown in Tip #5 can greatly improve the readability and maintainability of embedded software. The use of enum over a #define definition has many advantages. The use of enum is easy to maintain, port (even to C++) and enum will show up in the debugger watch windows. The ability to typedef an enum is a very powerful tool in C so use it wisely!
tip #7 – Don’t obfuscate the underlying type
One of the dangers of using typedef is that it is very easy to obfuscate the underlying type. For example, the typedef of a structure if the new type is not named properly can hide the fact that the data is a structure. Developers should be able to read and recognize what the underlying type of the data is without having to search through documentation or the source code. Looking back to Tip #4, obscuring volatile with v is the same idea. The resultant data type needs to make it obvious what it represents in a clear and concise manner.