5 Tips to Improve C Code Readability and Robustness

The quality of C code might be improving, but there are still some important ways to make it better still.

Jacob Beningo

January 24, 2021

6 Min Read
AdobeStock_C code.jpeg
Adobe Stock

Every year I read through and review quite a bit of C code. Overall, I feel the quality of the code I see today versus five years ago is improving but that could just be the code I’m exposed to, my influence on the teams I work with, or just wishful thinking. Despite the improvements I see, there are several issues I often encounter that could be remedied to improve code readability and robustness. Below are a few tips to consider when writing your C applications.

Tip #1 – Limit switch statement length

Switch statements are used to control the flow of code based on a value or evaluation of an expression. They are often used to replace a long if / else if / else statement or to determine which code should execute in a state machine. They are also often used in command execution in a system. It is not uncommon for me to come across switch statements having thousands of lines of code associated with them. These large switch statements often are difficult to read through and understand code execution, especially when the case statements contain evaluations, further branched logic, and function calls.

I often recommend switch statements be kept to 5 - 7 cases maximum and that each case either include a function or inline function associated with the case to simplify the switch statement. When switch statements grow behind the seven cases rule of thumb, which often happens in command processing, developers can instead switch to using an array of function pointers and index into the array to determine which function to execute. This improves the readability and flexibility of the code while decreasing its complexity.

Related:C Code Becomes User Accessible

Tip #2 – Place curly brackets on their own Lines

Curly bracket placement is a bit of a pet peeve of mine. I find it exceedingly difficult to quickly read through code and spot errors when code looks like the following:

if(variable >= value){

   variable = value;

}

When performing a quick read through, it is useful to lineup code and brackets so the code is easy to read. Quite a few programmers today seem to like to place the bracket on the same line, which I’m not sure if it’s for bragging rights that they wrote some function in fewer lines or what. From a code review standpoint and to ensure that brackets aren’t placed in the wrong place, placing these on separate lines dramatically improves readability as you can see below:

if(variable >= value)

{

   variable = value;

}

It is clearer, spacing out the code, and worst case, you can brag that you wrote an extra line of code, making it look like more work was done versus being more clever.

Tip #3 – Check function return values

When I first learned to program, it was drilled into my mind to check and validate inputs and outputs. I see a lot of code that will make a call to a function that will return a success or fail or provide some useful information, but the developer completely ignores it. As developers, we need to be checking every return value and not just assuming that everything went okay. I often see developers skip the return value and think they will just get this done and later go back and add the checks when they have more time. When it comes to product development, there is never “more time” so it is best to do the job right the first time.

Some compilers will complain when they set on a stricter setting that the return value is ignored. One of my least favorite things to see in a codebase is for a developer to resolve the warning by voiding out the return value and writing code something like:

// ignore the return value

(void) I2C_Write(Data);

If you are going to write the comment and the code to make the compiler happy, you might as well just write the code to retrieve and ensure everything went okay:

do

{

   Attempt++;

   ErrorCode = I2C_Write(Data);

}while((ErrorCode != SUCCESS) && (Attempt < RETRY_MAX));

Anyway, I think you get the idea.

Tip #4 – Use static for module scoped variables

The C programming language by default will throw an extern in front of functions and variables that are not declared static. Developers will often create variables at the top of a module that will be used throughout doing something like:

uint8_t MyVar = 0;

Entry-level developers will often not realize that this statement has an implicit extern in front of it and really looks to the compiler like the following:

extern uint8_t MyVar = 0;

If a variable will be only used within that module or accessed through helper function like get() or set(), declare the variable as static to limit its scope to the module:

static uint8_t MyVar = 0;

The same should be done with functions that will not be used outside the module. Place a static in front of them and do not put them in the header file!

Tip #5 – Write software with testing in mind

Embedded systems are becoming extraordinarily complex and it is nearly impossible to perform regression testing on a system if it is going to be done manually. Testing needs to be automated and that means developers need to write their software with testing in mind. Optimally, tests are written before the software using a technique like test-driven development, but personally, I am not even hoping for that much. In my mind just setting up a test harness and developing unit tests after a function is written is a good start.

As you write software with testing in mind, developers will notice that the code becomes a bit more abstract, layered, and the interfaces more well defined. If you are thinking about testing, it forces the software to take a certain shape which I’ve found results in a cleaner architecture and overall can help improve flexibility, scalability, and all those other features we are always striving to achieve.

Conclusions

The quality of C applications does appear to be improving, but there are still some basic techniques that developers can use to quickly improve their own code. Here, we examined just a few common ones I encounter that are quite easy to remedy. Make sure you take the time to implement these tips and you will find code reviews and even the robustness of your code will improve.

Jacob Beningo is an embedded software consultant who currently works with clients in more than a dozen countries to dramatically transform their businesses by improving product quality, cost, and time to market. He has published more than 200 articles on embedded software development techniques, is a sought-after speaker and technical trainer, and holds three degrees which include a Master of Engineering from the University of Michigan. Feel free to contact him at [email protected], at his website www.beningo.com, and sign-up for his monthly "Embedded Bytes Newsletter."

About the Author

Jacob Beningo

Jacob Beningo is an embedded software consultant who currently works with clients in more than a dozen countries to dramatically transform their businesses by improving product quality, cost and time to market. He has published more than 300 articles on embedded software development techniques, has published several books, is a sought-after speaker and technical trainer and holds three degrees which include a Masters of Engineering from the University of Michigan.

Sign up for Design News newsletters

You May Also Like