I understand that the title looks like a machine translation, but I could not think of a better equivalent to "Top 10 Bug-Killing Coding Standard Rules".
10 main rules for avoiding bugs - suggested in the comments
This post is a free retelling of the key concepts of the book by Michael Barr "Embedded C Coding Standard", presented in his speech at the
webinar in June of this year (I do not know how to put the tag "translation").
Some of the rules apply only to C ++ and C extensions, and some to the language standard.
Rule 1
The curly brackets {} ALWAYS must surround the block of code after the statements if, else, switch, while, do, for.
Corollary: A single statement and an empty expression should also be framed with curly braces.
Proof: Consider a simple code example.
if (5 == foo) bar(); always_run();
')
It seems to be okay, everything is clear and clear, but we decided to temporarily disable the call to the bar function (for example, when searching for a program crash location).
if (5 == foo)
and quite unexpectedly, the run function almost ceased to be called, and if the brackets were in place, this would not have happened.
if (5 == foo) {
The rule seems obvious and is repeatedly found in various sources, but how difficult it is to follow it, because the screen space is taken away under the empty line with a closing bracket, and this place is so pitiful. (Note that I, in violation of another rule, which is not included in the top 10, still added conditions to the operator and was able to save a whole line). A small note - I understand perfectly well that a string with a lonely closing parenthesis does not cost anything in the sense of compilation, but as you know, we write programs not for computers, but for other people, and I, for example, have a much clearer code when I can see the whole on screen without the need for rolling.
Rule 2
Use the const keyword wherever possible.
Consequences: We use when describing variables that will not change after initialization; when describing parameters of a function that should not be modified; when describing fields of structures and unions that cannot be modified (including structures describing device registers); as the recommended #define replacement for numeric constants.
Proof: this keyword does not cost anything when executed, in some systems it allows saving memory by placing in ROM, allows the compiler to detect invalid data operations, gives the reader additional information, takes up little space (does not require a separate line) - in short, there are no reason to neglect this rule ./* 1 /
char const *gp_model_name = “Acme 9000”; int const g_build_number = CURRENT_BUILD_NUMBER(); char *strncpy (char *p_dest, char const *p_src, size_t count); size_t const HEAP_SIZE = 8192;
Rule 3
Use the static keyword wherever possible.
Corollary: Use for ALL non-local variables and functions that should not be visible anywhere else except the module declaring them.
Proof: costs nothing at execution, improves encapsulation and data protection, protects a variable with a long lifetime from spelling of the name, * 1.
static uint32_t g_next_timeout = 0; static uint32_t add_timer_to_active_list(void)
Rule 4
Use the volatile keyword where it is needed (but not where it is possible — the translator's note).
Corollary: use when describing global variables that can be modified by the interrupt service routine; when describing global variables modified by more than one task; when describing registers of external devices mapped to memory.
Proof: it slows down the execution of commands with such variables (this is where the note comes from), but avoids hard-to-find errors, gives additional information to the reader, * 1.
uint16_t volatile g_state = SYSTEM_STARTUP; typedef struct { ... } my_fpga_t; my_fpga_t volatile *const p_timer = ...
Rule 5
Comments should not be used to disable a block of code, even temporarily.
Corollary: Use the conditional compilation directive of the preprocessor; do not use nested comments, even if your C dialect allows it; use the version control system to experiment with the code; Do not leave commented code in the release.
It seems that the rule is clear and simple to accomplish, but the hand is stretching to insert / * and * /, (especially since another rule - comments with the help of // I have already accepted) - it is necessary to fight the bad habit.
#if 0 a = a + 1; #endif
Rule 6
When the length, in bits or bytes, of integers is significant for a program, use data types with a specific length (C99 types).
Corollary: The keywords short and long should never be used; the char keyword is used only to define strings.
Proof: worthless on execution, facilitates portability, * 1.
#include < stdint.h > uint16_t data;
Rule 7
Never apply bitwise operations (&, |, ~, <<, >>) to signed types.
Corollary: You do not have to investigate the uncertain behavior of these operations.
Proof: worthless on execution, facilitates portability, * 1.
Rule 8
Never mix signed with unsigned types in expressions or comparisons.
Corollary: Unsigned digital constants must have a postfix u.
Proof: worthless on execution, you don’t have to remember the type allocation rules, * 1.
int s = - 9; unsigned int u = 6u; if (s + u < 4) {
Rule 9
Instead of using parameterized macros, use inline functions.
Corollary: in combination with rule 2, it eliminates the use of #define slightly less than completely (the compilation parameters remain).
Proof: macros are not debugable in principle and this is a very weighty argument, there is no type control when a macro is called, side effects are possible when modifying a parameter, but some C compilers perceive the inline directive as a wish and not as a guide to action - as a result, the execution time may increase. However, it is highly recommended for use.
#define SQUARE(A) ((A)*(A)) inline uint32_t square(uint16_t a) { return (a * a); }
Rule 10
We declare each variable on one line.
Corollary: A comma is not allowed in the variable list.
Proof: you don’t have to remember how * works; it improves the readability of the code.
However, in my opinion, the most ambiguous rule, since it increases the apparent size of the code. Even if the declarations are collected at the beginning of the module, (and there are also recommendations to declare local variables immediately before use), I personally cannot extend the code to the best parties, so I would continue to use declarations like:
int i,j,k;
But, of course, the following line is unacceptable:
int *pi, pj;
Summing up, we can say that we have a way to somewhat shorten the rope we have, without straining us to the impossibility of movement at all. Of course, one should not go as far as fanaticism in following the recommendations, one just has to keep in mind that it’s like in the Charter - each line is written in the blood of people who did it their own way. At the same time, none of the rules is in conflict with common sense; rather, they sometimes confront with ingrained habits, it is not at all necessary that they are rational.