
The most common task for hardware designers is to test hardware by writing code. These 10 C language tips (C remains a popular choice) can help designers avoid defects caused by fundamental errors, which can lead to maintenance issues.
To successfully launch a product, the software development process itself must navigate countless practical risks and obstacles. The last thing any engineer wants is to face challenges arising from the language or tools used.
Therefore, hardware designers need to write code to test the operational status of the hardware, and under resource constraints, they also need to develop hardware and embedded software.
Despite significant advancements in tools and structured programming, C language is still commonly chosen, and the continuous occurrence of fundamental errors can still lead to defects and maintenance issues. To avoid these C programming pitfalls, here are 10 C language tips for hardware engineers.
Tip 1: Avoid Using the “GOTO” Statement
Over twenty years ago, when computer programming was still in its infancy, program flow was controlled by “GOTO” statements. Such statements allowed programmers to break the current line of code and jump directly to another segment of code. List 1 provides a simple example.
List 1 Using the GOTO Statement

Programming languages eventually began to introduce the concept of functions, allowing programs to break code into segments. If completed, there is no longer a need to use GOTO statements to indicate code breaks.
After a function call, the function returns to the next instruction. List 2 provides an example. This practice improves program structure and enhances readability.
Since then, this has been regarded as the correct way to write programs. Just seeing or thinking about GOTO statements makes software engineers recoil with an instinctive aversion.
One of the main reasons is that a program filled with GOTO statements makes it difficult to grasp the main focus, complicating program understanding and maintenance.
List 2 Controlling Flow with Functions

Tip 2: Use FOR(;;) or While(1)
If GOTO statements are outdated, how should hardware engineers create infinite loops? This is a question some may wonder.
After all, previously, it was done by creating a GOTO statement and then returning to the main statement. The solution to this problem is to utilize the existing loop statements in C, for and while (Lists 3 and 4).
List 3 Using an Infinite For Loop

List 4 Using an Infinite While Loop

The loop conditions in the lists are relatively simple. The for loop simply uses a condition statement without any conditions. On the other hand, the while loop executes as long as the statement is true, which is equivalent to any non-zero value of the condition.
Tip 3: Use Appropriate Conditional Statements
Besides code readability, the execution time of a program also heavily depends on the type of conditional structure chosen when making decisions. Many hardware engineers are familiar with the use of simple if statements.
However, sometimes engineers may not realize that if the first condition is incorrect, they can also use else or else if statements. This can save processor time by avoiding the evaluation of another conditional statement.
In the first half of the code shown in List 5, if the value of Var is 1, the code will still check if Var is 0.
In the second half of the code using the else statement, only the first statement is evaluated, and then the code continues, saving clock cycles and making the code clearer.
List 5 Replacing If with If/Else

If/else if/else statements may not always be applicable. If several possible conditions need to be checked, a switch statement may be more appropriate.
This way, the processor can evaluate the statement and choose the next action from a list of answers without continuously evaluating a series of conditions. List 6 shows an example similar to that in List 5.
List 6 Using Switch Statements

The implication of the above examples is to make the selection of conditional statements more open to choose the most suitable statement. This practice simplifies program structure, making it easier to understand the program flow and reducing the extra clock cycles for the processor.
Tip 4: Avoid Using Assembly Language
The natural language of microprocessors is assembly language instructions. Programming in low-level machine language may provide more efficient code for the processor.
However, humans are not naturally adept at this language, and experience shows that writing in assembly language can lead to misunderstandings. Misunderstandings can lead to improper maintenance and, worse, may leave the system riddled with bugs. It is generally recommended to avoid using assembly language.
In fact, most compilers today can generate very efficient code. Developing in high-level languages like C or C++ can yield a more organized structure, making it easier to understand and maintain, resulting in better overall code.
List 7 provides an example comparing the assembly code and C code used to increment a 32-bit variable.
List 7 Incrementing a Variable in Assembly and C
Assembly
C Code

Of course, there are still some situations where using assembly language is appropriate, but these cases are relatively rare. The first recommended scenario is developing a bootloader. In this case, it may be necessary to optimize the speed of a decision during the startup process (whether to start an application or a bootloader). At this point, using assembly code for branching decisions may make sense.
Another scenario is developing a control loop running on a DSP with strict timing requirements. To achieve every clock cycle from the device, coding the control loop in assembly language makes sense.
If the current task is suitable for assembly, ensure it is well documented for future reference, so that future developers (or future versions) will understand the purpose of the code.
Tip 5: Make Full Use of Modularity
One of the most common experiences I encounter is that a new project initiated by hardware engineers often has a chaotic code organization. Typically, we find that the code consists of a single main module with over 25,000 lines of code.
In these applications, everything is global, with few functions, and GOTO statements permeate the entire code structure. Fifteen years ago, this was considered normal, but it is no longer applicable!
C programming allows engineers to break code into independent functional modules, simplifying code navigation while enabling the use of object-oriented techniques like encapsulation.
Code can be organized into logical modules, which makes a lot of sense. Although it may take a few minutes initially, in the long run, it will save many long nights and a lot of debugging pain!
Tip 6: Write Layered Code Instead of Spaghetti Code
Beningo is an Italian name, and like many Italians, I have an unabashed love for Italian pasta. When comparing pasta to software, I think of two types: spaghetti and lasagna.
Spaghetti is chaotic, with noodles intertwined in a way that lacks any structure. Writing unstructured code is very much like spaghetti: take a bite, and you have no idea which part you are eating.
The other type is lasagna! This pasta is layered and structured. Layered code is not only easier to understand but also allows for the removal of one layer and the addition of a new layer, essentially enabling reusability and ease of maintenance. Figure 1 provides a simple software module example using a layered code model.

Figure 1 Layered Software Model
Driver Configuration
Application Configuration
Application
Driver Library
Hardware
Tip 7: Use Descriptive Variable Names
Writing larger software that is easy to understand and maintain has many obstacles, one of which is naming conventions for variables. In an effort to shorten variable names, developers often create short, cryptic mnemonics that only they can understand.
Modern languages allow a variable name to contain hundreds of characters. To keep things clear, a straightforward approach is better than any other method. Therefore, having variable names that are self-explanatory benefits not only the developers but also future maintenance teams. List 8 provides an example.
List 8 Variable Naming

Tip 8: Use #pragma Statements Sparingly
In C language, there is a special #pragma statement. These statements typically deal with non-standard syntax and features and should be avoided as much as possible because they are non-standard and cannot be ported from one processor to another. Some compilers may require these statements to accomplish a task, such as defining an interrupt service routine.
In such cases, there may be no alternative but to use #pragma statements. If possible, place all #pragma statements in one module or a few modules.
This helps ensure that when porting code, only a few areas need to be updated rather than the entire codebase. Additionally, this will help prevent the confusion that can arise from the first compilation of ported code.
Tip 9: Errors Are Often Not as Simple as They Appear
When debugging a C program, one common pitfall is compiler errors. Due to the complexity of compilers, when an error is detected, it is often located elsewhere in the program rather than at the position indicated by the compiler.
This mainly relates to the steps the compiler takes to generate the program. The types of errors are often consistent, and among the errors that engineers can discover, 90% have the same root:
*Be careful not to miss #include files. This may cause developers to see perfect lines of code, but due to the absence of necessary header files, the compiler will flag it as an error, indicating something is undefined.
*Be careful not to miss semicolons. One of the most common errors when writing C code is forgetting to add a semicolon at the end of a statement.
*Be careful not to miss parentheses. Omitting parentheses is another common mistake in code writing, either due to carelessness or a typing error that produces an erroneous character.
*Be careful not to miss commas. It is easy to forget commas in complex definitions!
In general, when a strange compilation error dialog pops up, check the content that has already been compiled before that line. It is very likely that the error lies there! It may appear on the line above, in the middle, or in a completely different file.
Don’t give up! With some experience, solving these tricky issues will become second nature.
Tip 10: The Number of Lines of Code Written by Excellent Programmers Is Not Necessarily Fewer
There is a common misconception that an excellent programmer writes fewer lines of code to solve problems compared to an average programmer. Don’t fall into this erroneous thinking!
An excellent programmer typically possesses a meticulous thought process and clear structure in their coding foundation. Variable naming and encapsulation are appropriate, and global variables are almost nonexistent in the system. Functions should remain short and effective.
If the code appears messy and requires more lines to make it clearer, then feel free to write those extra lines!
You can look online for examples of code that has won awards for being the most chaotic in C programming as a cautionary tale. The code written by excellent programmers is concise, easy to understand, and maintain, and the number of lines of code is not the least (Figure 2)!

Figure 2 Short Programs


Some Screenshots from Electronic Books

【Complete Collection of Hardware Learning Materials】