Rules are not perfect. Disabling useful features in specific situations may affect code implementation. However, the rules are formulated "to help most programmers to get more benefits". If a rule is found unhelpful or difficult to follow in team coding, please send your feedback to us so we can improve the rule accordingly. Before referring to this guide, you are expected to have the following basic capabilities for C rather than being a beginner who wants to learn about C.
Rules are not perfect. They might disable useful features in specific situations and therefore affect code implementation. However, the purpose of developing rules is to get more benefits for most programmers. If a rule cannot be followed in your team operation, we can improve the rule together. Before referring to this coding style guide, you are expected to have the following basic capabilities of the C programming language:
1. Understand the ISO standard of C.
2. Be familiar with the basic features of C.
...
...
@@ -15,65 +15,72 @@ Code must meet the requirements for **readability**, **maintainability**, **secu
## <a name="c0-3"></a>Conventions
**Rule**: Conventions that must be followed during programming.
**Suggestion**: Conventions that must be considered during programming.
**Rec**: Conventions that must be considered during programming.
It is necessary to understand the reason for these conventions and to try and comply with them, no matter if they are rules or recommendations.
It is necessary to understand the reason for these conventions and try to comply with them, no matter if they are rules or recommendations.
## <a name="c0-4"></a>Exceptions
The only acceptable exceptions are those that do not violate the general principles and provide appropriate reasons for their existence.
Exceptions destroy code consistency. Try to avoid them. Exceptions to 'Rules' should be very rare.
Try to avoid exceptions because they affect the code consistency. Exceptions to 'Rules' should be very rare.
The style consistency principle is preferred in the following case:
**When you modify open source or third-party code. The existing code specifications prevail.**
**When you modify open-source or third-party code, comply with their respective code specifications.**
# <a name="c1"></a>1 Naming
Names include file, function, variable, type, and macro names.
Naming is considered the most difficult and important thing in software development.
The name of an identifier must be clear, well defined, and easy to understand, adapting to reading habit.
The name of an identifier must be clear, well defined, easy to understand, and accounting for reading habits.
The unified naming style is the most direct expression of the consistency principle.
## <a name="c1-1"></a>General Conventions
**CamelCase**
Camel case is the practice of writing compound words or phrases so that each word or abbreviation in the phrase begins with a capital letter, and with no intervening spaces or punctuation.
This style has two alternatives depending on the case of the first letter: **UpperCamelCase and lowerCamelCase**
CamelCase is the practice of writing compound words or phrases so that each word or abbreviation in the phrase begins with a capital letter, and with no intervening spaces or punctuation.
There are two conventions: **UpperCamelCase and lowerCamelCase**.
### <a name="r1-1"></a>Rule 1.1 Name identifiers in camel case style.
**Unix\_like**
Unix\_like is also known as the snake style. In the Unix\_like style, words contain only lowercase letters and are separated by underscores (\_).
Example: 'test_result'
### <a name="r1-1"></a>Rule 1.1 Name identifiers in the CamelCase style.
| Variable, function parameter, macro parameter, struct body, union member| lowerCamelCase
| Macro, constant, enumerated value, goto tag| All capitalized, separated with underscores (\_)
| Macro, constant, enumerated value, goto tag| All capitalized, separated by underscores (\_)
Note:
The `constant` in the above table refers to the variable of the basic data type, enumeration, and string type of the const modifier under the global scope, excluding array, struct and union.
The `variable` in the above table refers to variables other than the constant definition, all using lowercase.
**Constant** in the above table refers to the variable that is of the basic data type, enum type, and string type and modified by **const** under the global scope, excluding arrays, structs, and unions.
**Variable** indicates the variables excluding those defined in **Constant**. These variables use the lowerCamelCase style.
Unix\_like can be used for Linux or Unix friendly code.
For code that is using the Unix\_like style, you can continue using this style.
The same naming style must be used for the same function, struct, or union.
### <a name="a1-1"></a>Rec 1.1 The larger the scope, the more accurate the name should be.
### <a name="a1-1"></a>Rec 1.1 Use more accurate names for identifiers with a large scope.
C is different from C++. There is no namespace or class. Therefore, the names of identifiers in the global scope must not conflict with each other.
Names of global functions, global variables, macros, types, and enumerations must be accurately described and unique in the global scope.
Different from C++, C does not have namespace or class.Therefore, the names of identifiers in the global scope must not conflict with each other.
Names of global functions, global variables, macros, types, and enums must be accurately described and unique in the global scope.
Example:
```c
intGetCount(void);// Bad: inaccurate description.
intGetCount(void);// Bad: inaccurate description
intGetActiveConnectCount(void);// Good
```
For accurate naming, a module prefix can be added if necessary.
For purposes of accurate naming, a module prefix can be added if necessary.
The module prefix and the naming body can be connected by following the CamelCase style.
Example:
```c
intPrefixFuncName(void);// OK: CamelCase, with no prefix in the format, but prefix in the content.
intPrefixFuncName(void);// OK: CamelCase, a prefix in the content, but not in the format
enumXxxMyEnum{// OK.
enumXxxMyEnum{// OK
...
};
```
...
...
@@ -82,24 +89,24 @@ enum XxxMyEnum { // OK.
### <a name="a1-2"></a>Rec 1.2 Use lowercase file names.
File names naming are allowed only with lowercase letters, numbers, and underscores (\_).
Only lowercase letters, numbers, and underscores (\_) are allowed in file names.
File names should be as short, accurate, and unambiguous as possible.
The reason for using lowercase file names is that different systems process file names in different ways (for example, Microsoft DOS and Windows OS are not case sensitive, but Unix/Linux and Mac systems are by default).
The reason for using lowercase file names is that different systems process file names in different ways. (For example, file names in MS-DOS and Windows are not case sensitive, but those in Unix/Linux and macOS are case sensitive by default).
Good example:
`dhcp_user_log.c`
Bad examples:
`dhcp_user-log.c`: It is not recommended you separate names with '-'.
`dhcp_user-log.c`: It is not recommended that you separate words with the hyphen (-).
`dhcpuserlog.c`: The words are not separated, causing poor readability.
## <a name="c1-3"></a>Function Naming
Functions are named in UpperCamelCase style.
Functions are named in the UpperCamelCase style.
### <a name="a1-3"></a>Rec 1.3 Name functions that comply with reading habits.
### <a name="a1-3"></a>Rec 1.3 Name functions to comply with reading habits.
The "verb + object" structure can be used for action related function names. For example:
The "verb + object" structure can be used for action related function names. Example:
```c
AddTableEntry()// OK
...
...
@@ -107,7 +114,7 @@ DeleteUser() // OK
GetUserInfo()// OK
```
An adjective or a prefix "is" can be used in a function returning a Boolean value. For example:
An adjective or a prefix "is" can be used in a function returning a Boolean value. Example:
```c
DataReady()// OK
...
...
@@ -126,10 +133,10 @@ GetTotalCount() // OK
Variables are named in the lowerCamelCase style. This includes global variables, local variables, parameters in the function declaration or definition as well as parameters in function-like macro.
### <a name="r1-2"></a> Rule 1.2 Add the 'g\_' prefix to global variables. Do not add this prefix to static variables in a function.
### <a name="r1-2"></a>Rule 1.2 Add the 'g_' prefix to global variables, but not to static variables in a function.
Global variables should be used as little as possible, and special attention should be paid to their use. Prefixes are used for visual prominence, prompting developers to be more careful about using global variables.
The naming rule of global static variables is the same as that of global variables. The name of a static variable in a function is the same as that of a common local variable.
Global variables should be used as little as possible, and special attention should be paid to their use. This prefix highlights global variables so that developers can be more careful when handling them.
Global static variables and global variables are named in the same way. Static variables in functions and common local variables are named in the same way.
```c
intg_activeConnectCount;
...
...
@@ -141,18 +148,18 @@ void Func(void)
}
```
Notes: The nature of a constant is also a global variable, but it does not apply to current rule if the naming style is all uppercase and connected by underline.
Notes: Constants are also global variables in essence. However, if constants are named using uppercase letters separated by underscores (\_), the current rule does not apply.
### <a name="a1-4"></a>Rec 1.4 Keep local variables short and to the point.
The name of a local variable should be short on the premise that meanings can be expressed through context.
The following is an example:
Example:
```c
intFunc(...)
{
enumPowerBoardStatuspowerBoardStatusOfSlot;// Not good: Long redundant local variable.
enumPowerBoardStatuspowerBoardStatusOfSlot;// Not good: Long redundant local variable
powerBoardStatusOfSlot=GetPowerBoardStatus(slot);
if(powerBoardStatusOfSlot==POWER_OFF){
...
...
...
@@ -176,7 +183,7 @@ int Func(...)
```
Similarly, "tmp" can be used to address any type of temporary variable.
A short variable name should be used with caution, but sometimes a singlecharacter variable is allowed, for example, a counter variable in a loop statement.
A short variable name should be used with caution, but sometimes a single-character variable is allowed, for example, a counter variable in a loop statement.
```c
inti;
...
...
@@ -198,7 +205,7 @@ int Mul(int a, int b)
## <a name="c1-5"></a>Type Naming
Types are named in the UpperCamelCase style.
The type can be a structure, a union, or an enumeration.
The type can be a struct, a union, or an enum.
Example:
...
...
@@ -215,7 +222,7 @@ union Packet {
};
enumBaseColor{
RED,// Note: The enumeration is in UpperCamelCase style while the enumerated values adopt the macro naming style.
RED,// Note: The enum is in the UpperCamelCase style whereas the enumerated values adopt the macro naming style.
When you use typedef to set an alias for a struct, a union or an enum type, try to use anonymous type.
When you use `typedef` to set an alias for a struct, a union, or an enum, try to use the anonymous type.
If you need self-nesting pointers, you can add a 'tag' prefix or an underscore suffix.
```c
typedefstruct{// Good: The anonymous struct is used because self-nesting is not required.
inta;
intb;
}MyType;// Struct alias with UpperCamelCase.
}MyType;// The struct alias uses the UpperCamelCase style.
```c
typedefstructtagNode{// Good: Add the 'tag' prefix or use 'Node_'.
...
...
@@ -239,14 +246,18 @@ typedef struct tagNode { // Good: Add the 'tag' prefix or use 'Node_'.
}Node;// UpperCamelCase.
```
## <a name="c1-6"></a>Macros, Constants, and Enumeration Naming
## <a name="c1-6"></a>Macro, Constant, and Enum Naming
Macros and enumerated values are capitalized and are separated with underscores (\_).
Constants are recommended to be capitalized and be separated with underscores (\_). Also, as global const variables, they can be named with global variable style.
The constants here are defined as global const variables of basic data type, enumeration, and string type.
Use uppercase letters separated by underscores (\_) for macro names and enumerated values.
You are advised to use uppercase letters separated with underscores (\_) for constant names. Global const variables can be named with the same style of global variables.
The constants here are defined as global const variables of the basic data type, enum type, or string type.
Function-like macros, can be named the same way as functions, using the UpperCamelCase naming style.
However, this approach makes the macros look the same as functions. It is confusing and needs special attention.
Use uppercase letters separated by underscores (\_) for function-like macros.
Exceptions:
1. Functions that use macros to implement generic functions, for example, macros that implement functions such as list and map, can be named in the same way as functions, using the UpperCamelCase style.
2. A function-like macro that is used to replace a function in the earlier version can be named in the same way as functions, using the UpperCamelCase style.
3. Macros for printing logs can be named in the same way as functions, using the UpperCamelCase style.
Note: Function-like macros named in the UpperCamelCase style must be marked as macros in the API description.
Macro example:
...
...
@@ -258,7 +269,7 @@ Macro example:
```c
#ifdef SOME_DEFINE
voidBar(int);
#define Foo(a) Bar(a) // The function-like macro is named as a function style.
#define Foo(a) Bar(a) // The function-like macro is named in the same way as a function.
#else
voidFoo(int);
#endif
...
...
@@ -269,34 +280,34 @@ Constant example:
```c
constintVERSION=200;// OK.
constenumColorDEFAULT_COLOR=BLUE;// OK.
constenumColorDEFAULT_COLOR=BLUE;// OK
constcharPATH_SEP='/';// OK.
constcharPATH_SEP='/';// OK
constchar*constGREETINGS="Hello, World!";// OK.
constchar*constGREETINGS="Hello, World!";// OK
```
Non-constant example:
```c
// Structure type does not meet the definition of constant.
// A struct that does not meet the definition of constants
conststructMyTypeg_myData={...};// OK: Name it in lowerCamelCase style.
// Array type does not meet the definition of constant.
// An array that does not meet the definition of constants
constintg_xxxBaseValue[4]={1,2,4,8};// OK: Name it in lowerCamelCase style.
intFoo(...)
{
// The scope does not meet the definition of constant.
// A local const variable that does not meet the definition of constants
constintbufSize=100;// OK: Name it in lowerCamelCase style.
...
}
```
Enumeration example:
Enum example:
```c
// Note: The enumeration type name is in the UpperCamelCase style, while enumerated values are all capitalized and separated with underscores (_).
// Note: The enum type name is in the UpperCamelCase style, whereas the enumerated value is in uppercase letters separated by underscores (\_).
enumBaseColor{
RED,
GREEN,
...
...
@@ -304,19 +315,16 @@ enum BaseColor {
};
```
### <a name="a1-5"></a>Rec 1.5 Do not name temporary variables in function-like macros and pollute the external scope.
First, **use function-like macros as little as possible.**
### <a name="a1-6"></a>Rec 1.5 Avoid temporary variables in function-like macros from polluting external scopes.
When a function-like macro needs to define local variables, to avoid naming conflicts with local variables in external functions,
an underline is a good solution. Example:
**If possible, use a function instead of a function-like macro. Define a function-like macro only when necessary.**
When defining local variables for a function-like macro, use double underscores at the end to avoid name conflicts with local variables in external functions. Example:
```c
#define SWAP_INT(a, b) do { \
int tmp_ = a; \
int tmp__ = a; \
a = b; \
b = tmp_; \
b = tmp__; \
} while (0)
```
...
...
@@ -324,17 +332,17 @@ an underline is a good solution. Example:
## <a name="c2-1"></a>Line Length
### <a name="a2-1"></a>Rec 2.1 Ensure that each line is no more than 120 characters in length.
### <a name="r2-1"></a>Rule 2.1 Include 120 characters or less in each line.
The line width of the code should not be too long, otherwise it is not conducive to reading.
The line length requirement indirectly guides developers in shortening function and variable names, reducing nesting, and improving readability.
It is strongly recommended that the number of characters per line do not exceed **120** unless readability is significantly improved as a result and no information is hidden.
While the resolution of modern monitors is very high, long lines will increase the difficulty of reading comprehension, which is against the principles of "clear" and "concise" defined in this document.
A longer line makes it more difficult for reading.
To meet the line length requirement, you can shorten the names of functions and variables and reduce the number of nesting layers. This improves code readability.
Unless a long line is necessary to maintain readability and present complete information, steer your document clear of long lines.
Even on a high-resolution monitor, a long line increases the difficulty of reading. Strive for clearness and conciseness.
The following scenarios should not be wrapped, and can be treated as exceptions:
Exceptions:
-Line breaks can cause strings truncated and hard to retrieved (grep), such as command lines or URLs. Codes or comments that contain these can be treated as exceptions appropriately.
-'#include', '#error' statements are allowed to exceed the line width requirement, but you should try to avoid this.
-For code lines or comments, the use of the line feed causes content truncation and increases the search difficulty (grep).
-The #include and #error statements are allowed to exceed the line length requirement. However, you should try to avoid this.
Example:
...
...
@@ -346,23 +354,23 @@ Example:
## <a name="c2-2"></a>Indentation
### <a name="r2-1"></a>Rule 2.1 Use spaces to indent and indent four spaces at a time.
### <a name="r2-2"></a>Rule 2.2 Use spaces to indent and indent four spaces at a time.
Only spaces can be used for indentation. **4** spaces are indented each time. Do not use the Tab character to indent.
Currently, almost all integrated development environments (IDEs) and code editors support automatically converting a Tab input to **4** spaces. Please set your code editor to support indentation with spaces, if it is not the default.
Only spaces can be used for indentation. Four spaces are indented each time. Do not use the Tab character to indent.
Currently, almost all integrated development environments (IDEs) and code editors support automatic conversion of a Tab input to fours spaces. Configure your code editor to support indentation with spaces.
## <a name="c2-3"></a>Braces
### <a name="r2-2"></a>Rule 2.2 Use the K\&R indentation style.
### <a name="r2-3"></a>Rule 2.3 Use the K\&R indentation style.
**K\&R style**
While wrapping a line, the left brace of the function starts a new line and takes a single line. Other left braces are placed at the end of the line along with the statement.
The right brace takes a single line, unless it is followed by the rest of the same statement, such as 'while' in the 'do' statement, or 'else'/'else if' of the 'if' statement, or a comma or semicolon.
The right brace takes a single line, unless it is followed by the rest of the same statement, such as `while` in the `do` statement, `else` or `else if` in the `if` statement, a comma, or a semicolon.
For example:
Example:
```c
structMyType{// Good: Follow the statement to the end, and indent one space.
structMyType{// Good: The left brace is placed at the end of the line along with the statement, and one space is used for indentation.
...
};// Good: The right brace is followed by the semicolon.
...
...
@@ -370,7 +378,7 @@ int Foo(int a)
{// Good: The left brace of the function starts a new line, and nothing else is placed on the line.
if(...){
...
}else{// Good: The 'else' statement follows the right brace.
}else{// Good: The right brace is followed by the `else` statement.
...
}// Good: The right brace takes a single line.
}
...
...
@@ -378,36 +386,36 @@ int Foo(int a)
## <a name="c2-4"></a>Function Declaration and Definition
### <a name="r2-3"></a>Rule 2.3 The return type and function name of the function declaration and definition must be on the same line. The function parameter list must be aligned appropriately if it needs to be wrapped.
### <a name="r2-4"></a>Rule 2.4 Keep the return type and function name of the function declaration or definition in the same line, and align the function parameter list appropriately if it needs to be wrapped.
When a function is declared and defined, the return value type of the function should be in the same line as the function name.
When the function parameter list is wrapped, it should be aligned appropriately.
The left parenthesis of a parameter list is always in the same line as the function name. The right parenthesis always follows the last parameter.
The following is an example of line breaks:
Example:
```c
ReturnTypeFunctionName(ArgTypeparamName1,ArgTypeparamName2)// Good:All in one line
ReturnTypeFunctionName(ArgTypeparamName1,ArgTypeparamName2)// Good: All in one line
{
...
}
ReturnTypeVeryVeryVeryLongFunctionName(ArgTypeparamName1,// The line length cannot accommodate all parameters and thus a line break is required.
ArgTypeparamName2,// Good:Aligned with the previous line.
ReturnTypeVeryVeryVeryLongFunctionName(ArgTypeparamName1,// Each added parameter starts on a new line because the line length limit is exceeded.
ArgTypeparamName2,// Good: Aligned with the previous line
ArgTypeparamName3)
{
...
}
ReturnTypeLongFunctionName(ArgTypeparamName1,ArgTypeparamName2,// Subject to line length, a line break is required.
ArgTypeparamName3,ArgTypeparamName4,ArgTypeparamName5)// Good: After the line break, 4 spaces are used for indentation.
ReturnTypeLongFunctionName(ArgTypeparamName1,ArgTypeparamName2,// Parameters are wrapped because the line length limit is exceeded.
ArgTypeparamName3,ArgTypeparamName4,ArgTypeparamName5)// Good: 4 spaces are used for indentation.
{
...
}
ReturnTypeReallyReallyReallyReallyLongFunctionName(// The line length cannot accommodate the first parameter, and thus a line break is required.
ArgTypeparamName1,ArgTypeparamName2,ArgTypeparamName3)// Good: After the line break, 4 spaces are used for indentation.
ReturnTypeReallyReallyReallyReallyLongFunctionName(// The line length cannot accommodate even the first parameter, and a line break is required.
ArgTypeparamName1,ArgTypeparamName2,ArgTypeparamName3)// Good: 4 spaces are used for indentation.
{
...
}
...
...
@@ -415,40 +423,40 @@ ReturnType ReallyReallyReallyReallyLongFunctionName( // The line leng
## <a name="c2-5"></a>Function Calls
### <a name="r2-4"></a>Rule 2.4 The parameter list should be aligned appropriately when the parameter list requires a line break.
### <a name="r2-5"></a>Rule 2.5 Align the parameter list appropriately if it needs to be wrapped.
When the function is called, if the function parameter list is wrapped, it should be aligned appropriately.
In a function call, if the function parameter list is wrapped, it should be aligned appropriately.
The left parenthesis is always followed by a function name, and the right parenthesis always follows the last parameter.
The following is an example of line breaks:
Example:
```c
ReturnTyperesult=FunctionName(paramName1,paramName2);// Good:Function parameters are placed in one line.
ReturnTyperesult=FunctionName(paramName1,paramName2);// Good: Function parameters are placed in one line.
ReturnTyperesult=FunctionName(paramName1,
paramName2,// Good:Aligned with the above parameters.
paramName2,// Good: Aligned with the above parameters
paramName3,paramName4,paramName5);// Good:After the line break, 4 spaces are used for indentation.
paramName3,paramName4,paramName5);// Good: 4 spaces are used for indentation.
ReturnTyperesult=VeryVeryVeryLongFunctionName(// The line length cannot accommodate the first parameter, and thus a line break is required.
paramName1,paramName2,paramName3);// After the line break, 4 spaces are used for indentation.
ReturnTyperesult=VeryVeryVeryLongFunctionName(// The line length cannot accommodate the first parameter, and therefore line feed is used.
paramName1,paramName2,paramName3);// 4 spaces are used for indentation.
```
If the parameters in a function call are associated with each other, the parameters are grouped for better understanding, rather than strictly adhering to formatting requirements.
If the parameters in a function call are associated with each other, you can group the parameters for better understanding, rather than strictly adhering to the formatting requirements.
```c
// Good:The parameters in each line represent a group of data structures with a strong correlation. They are placed on one line for ease of understanding.
// Good: The parameters in each line represent a group of data structures with a strong correlation.They are placed on one line for ease of understanding.
intresult=DealWithStructureLikeParams(left.x,left.y,// Indicates a group of parameters.
right.x,right.y);// Indicates another group of related parameters.
```
## <a name="c2-6"></a>Conditional Statements
### <a name="r2-5"></a>Rule 2.5 Conditional statements must use braces.
### <a name="r2-6"></a>Rule 2.6 Use braces for conditional statements.
We require that all conditional statements use braces, even if there is only one statement.
Use braces to enclose conditional statements, even if there is only one statement.
Reason:
- Logic is intuitive and easy to read.
...
...
@@ -461,21 +469,21 @@ if (objectIsNotExist) { // Good: Braces are added to a single-line condi
}
```
### <a name="r2-6"></a>Rule 2.6 Do not place 'if', 'else' and 'else if' in the same line.
### <a name="r2-7"></a>Rule 2.7 Do not place `if`, `else`, and `else if` in the same line.
In a conditional statement, if there are multiple branches, they should be written in different lines.
In a conditional statement, branches, if any, should be written in different lines.
The correct format:
Good example:
```c
if(someConditions){
...
}else{// Good: The 'else' is in a different line of 'if'.
}else{// Good: The `else` statement is in a different line of `if`.
...
}
```
The following is a case that does not comply with the specifications:
Bad example:
```c
if(someConditions){...}else{...}// Bad: They are in the same line.
...
...
@@ -483,9 +491,9 @@ if (someConditions) { ... } else { ... } // Bad: They are in the same line.
## <a name="c2-7"></a>Loops
### <a name="r2-7"></a>Rule 2.7 Use braces in loop statements.
### <a name="r2-8"></a>Rule 2.8 Use braces for loop statements.
Similar to the condition expression, we require that the for and while loop conditional statements contain braces, even if there is only one loop.
Use braces to enclose the `for` and `while` statements, even if there is only one loop.
```c
for(inti=0;i<someRange;i++){// Good: Braces are used.
...
...
@@ -494,7 +502,7 @@ for (int i = 0; i < someRange; i++) { // Good: Braces are used.
```
```c
while(condition){}// Good: The while loop body is empty. And braces are used.
while(condition){}// Good: The entire loop body is empty. And braces are used.
```
```c
...
...
@@ -511,14 +519,14 @@ for (int i = 0; i < someRange; i++)
```
```c
while(condition);// Bad: Using semicolons will make people misunderstand which code is a part of the while statement.
while(condition);// Bad: The semicolon may be treated as part of the `while` statement.
```
## <a name="c2-8"></a>Switch Statements
## <a name="c2-8"></a>`switch` Statements
### <a name="r2-8"></a>Rule 2.8 Indent the case or default statement in a switch statement block.
### <a name="r2-9"></a>Rule 2.9 Indent the `case` or `default` statement in a `switch` statement block.
The indentation style of the switch statement is as follows:
Use the following indentation style for the `switch` statement:
```c
switch(var){
...
...
@@ -546,16 +554,15 @@ default: // Bad: 'default' not indented
## <a name="c2-9"></a>Expressions
### <a name="a2-2"></a>Rec 2.2 Keep a consistent expression line break style and ensure that operators are placed at the end of the line.
### <a name="a2-1"></a>Rec 2.1 Keep a consistent line break style for expressions and ensure that operators are placed at the end of the line.
A long expression that does not meet the line length requirement must be wrapped appropriately. Generally, the expression is wrapped at an operator of a lower priority or a hyphen, and the operator or hyphen is placed at the end of the line.
The operator and hyphen are placed at the end of the line, indicating that the operation is to be continued.
A long expression that does not meet the line length requirement must be wrapped appropriately. Generally, the expression is wrapped after a lower-priority operator or a hyphen, and the operator or hyphen is placed at the end of the line, indicating that the operation is to be continued.
Example:
```c
// Pretend that the following first line does not meet the line length requirement.
if((currentValue>MIN)&&// Good: After the line break, the Boolean operator is placed at the end of the line.
// Assume that the first line does not meet the line length requirement.
if((currentValue>MIN)&&// Good: The Boolean operator is placed at the end of the line.
(currentValue<MAX)){
DoSomething();
...
...
...
@@ -565,7 +572,7 @@ int result = reallyReallyLongVariableName1 + // Good: The plus sign is placed
reallyReallyLongVariableName2;
```
After an expression is wrapped, ensure that the lines are properly aligned or indented by 4 spaces. See the following example.
After an expression is wrapped, ensure that the lines are properly aligned or indented by 4 spaces. Example:
@@ -577,32 +584,32 @@ int sum = longVaribleName1 + longVaribleName2 + longVaribleName3 +
## <a name="c2-10"></a>Variable Assignment
### <a name="r2-9"></a> Rule 2.9 Multiple variable definitions and assignment statements cannot be written on one line.
### <a name="r2-10"></a> Rule 2.10 Do not write multiple variable definitions or assignment statements in one line.
It is best to have only one variable initialization statement on each line. It is easier to read and understand.
It is recommended that each line contain only one variable initialization statement, which is easier to read and understand.
```c
intmaxCount=10;
boolisCompleted=false;
```
The following is an example that does not comply with the specifications:
Bad example:
```c
intmaxCount=10;boolisCompleted=false;// Bad: Multiple initialization statements are placed on the same line.
intx,y=0;// Bad: Variable definitions need to be placed on different lines. Each definition occupies one line.
intmaxCount=10;boolisCompleted=false;// Bad: Multiple initialization statements are placed in one line.
intx,y=0;// Bad: Multiple variable definitions are placed in one line.Each definition occupies one line.
intpointX;
intpointY;
...
pointX=1;pointY=2;// Bad: Multiple variable assignment statements are placed on the same line.
pointX=1;pointY=2;// Bad: Multiple variable assignment statements are placed in one line.
```
Exception:
If multiple variables with strong correlation are defined and do not need to be initialized, you can define the variables in a line to reduce repeated information so that the code is more compact.
Exceptions:
If multiple variable definitions have strong correlation and do not need to be initialized, you can define the variables in a line for code compactness.
```c
inti,j;// Good: Multiple variables that are defined and do not need to be initialized immediately can be written in one line.
inti,j;// Good: Multiple variable definitions that do not need to be initialized are written in one line.
for(i=0;i<row;i++){
for(j=0;j<col;j++){
...
...
...
@@ -614,16 +621,16 @@ for (i = 0; i < row; i++) {
Initialization is applicable to structs, unions, and arrays.
### <a name="r2-10"></a>Rule 2.10 Indent when initiating a new line, or make a reasonable alignment.
### <a name="r2-11"></a>Rule 2.11 Use indentation or make a reasonable alignment for a new line.
When a structure or array is initialized, if a line break is made, ensure that the line is indented with 4 spaces.
From the readability point of view, make a reasonable alignment.
For the struct or array initialization, use 4 spaces for indentation if a line break is made.
From better readability, make a reasonable alignment.
```c
// Good: No line break for a short line.
intarr[4]={1,2,3,4};
// Good: A line break here makes the readability better.
// Good: A line break makes better readability.
constintrank[]={
16,16,16,16,32,32,32,32,
64,64,64,64,32,32,32,32
...
...
@@ -635,12 +642,12 @@ Refer to the following format:
```c
inta[][4]={
{1,2,3,4},{2,2,3,4},// OK.
{1,2,3,4},{2,2,3,4},// OK
{3,2,3,4},{4,2,3,4}
};
intb[][8]={
{1,2,3,4,5,6,7,8},// OK.
{1,2,3,4,5,6,7,8},// OK
{2,2,3,4,5,6,7,8}
};
```
...
...
@@ -648,7 +655,7 @@ int b[][8] = {
```c
intc[][8]={
{
1,2,3,4,5,6,7,8// OK.
1,2,3,4,5,6,7,8// OK
},{
2,2,3,4,5,6,7,8
}
...
...
@@ -657,12 +664,12 @@ int c[][8] = {
Note:
- If the left brace is placed at the end of the line, the corresponding right brace should be placed into a new line.
- If the left brace is placed at the end of the line, the corresponding right brace shoud be placed into a new line.
- If the left brace is followed by the content, the corresponding right brace should also follow the content.
### <a name="r2-11"></a>Rule 2.11 When struct and union members are initialized, each member is initialized on a separate line.
### <a name="r2-12"></a>Rule 2.12 Initialize each member in a separate line during struct and union member initialization.
The C99 standard supports the initialization of the struct and union members in their definition. This is called the designated initializer. If initialization is performed in this way, each member is initialized in a separate line.
The C99 standard supports the initialization of the struct and union members in their definition. This is called the designated initializer. In such a way, each member should be initialized in a separate line.
```c
structDate{
...
...
@@ -671,7 +678,7 @@ struct Date {
intday;
};
structDatedate={// Good: When the designated initializer is used, each member is initialized on a separate line.
structDatedate={// Good: When the designated initializer is used, each member is initialized in a separate line.
.year=2000,
.month=1,
.day=1
...
...
@@ -680,104 +687,75 @@ struct Date date = { // Good: When the designated initializer is used, each m
## <a name="c2-12"></a>Pointers
### <a name="a2-3"></a>Rec 2.3 The pointer type asterisk "\*" follows the variable name or the type. Do not leave spaces on both sides and always use at least one space.
### <a name="a2-2"></a>Rec 2.2 Ensure that the asterisk (\*) in the pointer type is followed by the variable name or follows the type. There must be a space before or after the asterisk.
When you declare or define a pointer variable or return a pointer type function, "\*" can be placed on the left or right, adhering to the type or name. Do not leave spaces on both sides. And do not leave out spaces altogether.
When you declare or define a pointer variable or return a pointer type function, the asterisk can be placed on the left (following the type) or right (followed by the variable name). There must be only one space before or after the asterisk.
```c
int*p1;// OK.
int*p2;// OK.
int*p1;// OK
int*p2;// OK
int*p3;// Bad: No spaces.
int*p4;// Bad: Spaces on both sides.
int*p3;// Bad: No space
int*p4;// Bad: Space on both sides
```
Choose a style and stay consistent.
When using style that "\*" follows type, avoid declaring multiple variables with pointer in a line.
If you use the asterisk to follow the type, do not declare multiple variables with pointers in a line.
```c
int*a,b;// Bad: It is easy to misinterpret b as a pointer.
int*a,b;// Bad: b may be treated as a pointer.
```
When using style that "\*" follows variable, there may be situations that cannot be followed.
Do not follow when it cannot. Style consistency first.
Do not use the asterisk followed by the variable name if this style is not suitable in all cases.
Keep style consistency first.
```c
char*constVERSION="V100";// OK.
intFoo(constchar*restrictp);// OK.
char*constVERSION="V100";// OK
intFoo(constchar*restrictp);// OK
```
"\*" never follows 'const' or 'restrict' keywords anytime.
Do not use the asterisk to follow the `const` or `restrict` keywords.
## <a name="c2-13"></a>Compilation Preprocessing
### <a name="r2-12"></a>Rule 2.12 The number sign (#) must be placed at the beginning of a line for compilation preprocessing and can be indented in nested compilation preprocessing.
### <a name="r2-13"></a>Rule 2.13 Place the number sign (#) at the beginning of a line for compilation preprocessing. In nested compilation preprocessing, the number sign (#) can be indented.
The number sign (#) must be placed at the beginning of a line for compilation preprocessing, even if the code is embedded in the function body.
```c
#if defined(__x86_64__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) // Good: "#" is at the beginning of the line.
#define ATOMIC_X86_HAS_CMPXCHG16B 1 // Good: "#" is at the beginning of the line.
#else
#define ATOMIC_X86_HAS_CMPXCHG16B 0
#endif
intFunctionName(void)
{
if(someThingError){
...
#ifdef HAS_SYSLOG // Good: "#" is at the beginning of the line even though it's in a function body.
WriteToSysLog();
#else
WriteToFileLog();
#endif
}
}
```
Nested preprocessing statements starting with "#" can be indented and aligned based on indentation requirements to different layers.
#define ATOMIC_X86_HAS_CMPXCHG16B 1 // Good: Statements are layered, facilitating reading.
#else
#define ATOMIC_X86_HAS_CMPXCHG16B 0
#endif
```
Try your best not to use compilation preprocessing macros. If they are needed in deed, they should be managed by dedicated personnel in a unified manner.
## <a name="c2-14"></a>Whitespace
### <a name="r2-13"></a>Rule 2.13 Ensure that horizontal whitespace is used to highlight keywords, important information and avoid unnecessary whitespace.
### <a name="r2-14"></a>Rule 2.14 Use horizontal whitespaces to highlight keywords and important information, and avoid unnecessary whitespaces.
Horizontal spaces should be used to highlight keywords and important information. Do not add spaces at the end of each line of code. The general rules are as follows:
- Add spaces after keywords such as if, switch, case, do, while, and for.
- Add spaces after keywords such as `if`, `switch`, `case`, `do`, `while`, and `for`.
- Do not add spaces after the left parenthesis or before the right parenthesis.
- Add a space before and after each binary operator (= + - \< > \* / % \|\&\^\<= >= == !=).
- Do not add a space after any unary operator (\&\* + - ~!).
- A space is required before and after each ternary operator (? :).
- Add a space before and after each ternary operator (? :).
- Add spaces before and after the colon of bit field description.
-There is no space between ++/-- and variables.
-There is no space before and after the struct member operator (. ->).
-Do not add spaces between ++/-- and variables.
-Do not add spaces before and after the struct member operator (. ->).
- Adding or not adding spaces inside the brace must be consistent.
- Do not add spaces before commas, semicolons, colons (without the colon in the ternary operator or the bit field description); Add spaces after them.
-There is no space between the parentheses of the function parameter list and the function name.
-There is no space between the parenthesis of the type cast and the object being converted.
-There is no space between the square bracket of the array and the array name.
- Do not add spaces before commas, semicolons, or colons (excluding the colon in the ternary operator or the bit field description). Add spaces after them.
-Do not add spaces between the parentheses of the function parameter list and the function name.
-Do not add spaces between the parenthesis of the type cast and the object being converted.
-Do not add spaces between the square bracket of the array and the array name.
- Spaces at the end of the line can be omitted.
For spaces inside the braces, the following are **recommended**:
For spaces inside the braces, the following **recommendations** are available:
- In general, spaces should be added after the left brace or before the right brace.
- For empty, or a single identifier, or a single literal constant, spaces are not required. Such as: '{}', '{0}', '{NULL}', '{"hi"}'.
- Spaces between consecutively nested multiple parentheses, spaces are not required. Such as: '{{0}}', '{{ 1, 2 }}'. Bad example: '{ 0, {1}}'. It is not a continuous nested scene, and the spaces inside the outermost braces are inconsistent.
- It is recommended that spaces be added after the left brace and before the right brace.
- For an empty constant or a constant with a single identifier or a single word, spaces are not required, for example, '{}', '{0}', '{NULL}', '{"hi"}'.
- Spaces between consecutively nested multiple parentheses are not required, for example, '{{0}}', '{{ 1, 2 }}'. '{ 0, {1}}' is a bad example, since it is not a consecutively nested scene and the spaces inside the outermost braces are inconsistent.
In normal cases:
```c
inti=0;// Good: When the variable is initialized, there should be spaces before and after the =. Do not leave a space before the semicolon.
intbuf[BUF_SIZE]={0};// Good: During array initialization, spaces in curly braces are optional.
intbuf[BUF_SIZE]={0};// Good: For array initialization, spaces in curly braces are optional.
intarr[]={10,20};// Good: A space is added before and after the brace.
```
...
...
@@ -785,7 +763,7 @@ Function definition and call:
```c
intresult=Foo(arg1,arg2);
^// Bad: There should be a space after the comma.
^// Bad: There is no space after the comma.
intresult=Foo(arg1,arg2);
^^// Bad: No space should be added to either side in the parentheses.
...
...
@@ -794,42 +772,42 @@ int result = Foo( arg1, arg2 );
Pointer and address-of operator:
```c
x=*p;// Good: There is no space between the operator * and the pointer p.
p=&x;// Good: There is no space between the operator & and the variable x.
x=*p;// Good: There is no space between the operator (*) and the pointer p.
p=&x;// Good: There is no space between the operator (&) and the variable x.
x=r.y;// Good: When a member variable is accessed through the operator (.), no space is added.
x=r->y;// Good: When a member variable is accessed through the operator (.), no space is added.
x=r->y;// Good: When a member variable is accessed through the operator (->), no space is added.
```
Operator:
```c
x=0;// Good: A space must be added before and after the assignment operator (=).
x=-5;// Good: Do not add spaces before the minus sign (-) and the number.
++x;// Good: Do not add spaces before the minus sign (-) and the number.
x=0;// Good: There is a space before and after the assignment operator (=).
x=-5;// Good: There is no space between the minus sign (-) and the number.
++++x;// Good: There is no space between ++/-- and the variable.
x--;
if(x&&!y)// Good: A space must be added before and after the Boolean operator. Do not leave spaces between the ! operator and variables.
v=w*x+y/z;// Good: A space must be added before and after binary operators.
v=w*(x+z);// Good: No space is required before and after the expression in the parentheses.
if(x&&!y)// Good: There is a space before and after the Boolean operator, and there is no space between the operator (!) and variable.
v=w*x+y/z;// Good: There is a space before and after binary operators.
v=w*(x+z);// Good: There is no space before and after the expression in the parentheses.
```
Loops and conditional statements:
```c
if(condition){// Good: A space is added between the if keyword and the parenthesis and no space is added before or after the conditional statement inside the parentheses.
if(condition){// Good: A space is added between the `if` keyword and the parenthesis, and no space is added before or after the conditional statement inside the parentheses.
...
}else{// Good: A space is added between the else keyword and the curly brace.
}else{// Good: A space is added between the `else` keyword and the curly brace.
...
}
while(condition){}// Good: A space is added between the while keyword and the parenthesis. No space is added before or after the conditional statement inside the parentheses.
while(condition){}// Good: A space is added between the `while` keyword and the parenthesis, and no space is added before or after the conditional statement inside the parentheses.
for(inti=0;i<someRange;++i){// Good: A space is added between the for keyword and the parenthesis, and after the semicolons (;).
for(inti=0;i<someRange;++i){// Good: A space is added between the `for` keyword and the parenthesis, and after the semicolons (;).
...
}
switch(var){// Good: A space is added after the switch keyword.
case0:// Good: No space is added between the case conditional statement and the colon.
switch(var){// Good: A space is added after the `switch` keyword.
case0:// Good: No space is added between the `case` conditional statement and the colon (:).
...
break;
...
...
...
@@ -839,9 +817,9 @@ switch (var) { // Good: A space is added after the switch keyword.
}
```
Note: Current integrated development environments (IDEs) and code editors can be set to automatically delete spaces at the end of a line. Please configure your editor as such.
Note: The current IDE and code editor can be set to delete spaces at the end of a line. Configure your editor correctly.
### <a name="a2-3"></a>Rec 2.3 Arrange blank lines reasonably to keep the code compact.
Reduce unnecessary blank lines so that more code can be displayed for easy reading. The following rules are recommended:
...
...
@@ -890,22 +868,28 @@ Comments help readers quickly understand code. Therefore, **comments should be p
The comments must be concise, clear, and unambiguous, ensuring that the information is complete and not redundant.
**Comments are as important as code.**
When writing a comment, you need to step into the reader's shoes and use comments to express what the reader really needs. Comments are used to express the function and intention of code, rather than repeating code.
When modifying the code, ensure that the comments are consistent. It is impolite to only modify code and not update the comments. It destroys the consistency between code and comments, and may confuse or even mislead readers.
When writing a comment, you need to step into the reader's shoes and use comments to express what the reader really needs. Comments are used to express the code functionality and intention, rather than repeating code.
When modifying the code, ensure that the comments are consistent. It is impolite to only modify code and not update the comments. This destroys the consistency between code and comments, and may cause confusion or even misleading.
Comment code in fluent English.
Comment code in fluent **English**.
Comments must be added in the following scenarios (including but not limited to the listed scenarios):
1. Functions in the external interface header file provided by the module
2. Global variables being defined
3. Core algorithms
4. A function that contains more than 50 lines
## <a name="c3-1"></a>Comment Style
In C code, both `/*``*/` and `//` can be used.
Comments can be classified into different types depending on purpose and position, such as file header comments, function header comments, and general comments.
Comments can be classified into different types depending on the purpose and position, such as file header comments, function header comments, and general comments.
Comments of the same type must keep a consistent style.
Note: The sample code used in this article sees extensive use of the '//' post-comment. This is only to aid understanding, and does not mean that this comment style is better.
Note: The sample code used in this article sees extensive use of the '//' post-comment. This is only for better understanding, and does not mean that this comment style is better.
## <a name="c3-2"></a>File Header Comments
### <a name="r3-1"></a>Rule 3.1 File header comments must contain the copyright license.
### <a name="r3-1"></a>Rule 3.1 Include the copyright license in the file header comments.
/\*
...
...
@@ -924,7 +908,7 @@ Note: The sample code used in this article sees extensive use of the '//' post-c
## <a name="c3-3"></a>Function Header Comments
### <a name="r3-2"></a>Rule 3.2 Empty function header comments with no content are forbidden.
### <a name="r3-2"></a>Rule 3.2 Do not use empty function header comments with no content.
Not all functions need function header comments.
Function header comments must be added for any information that cannot be expressed just with function prototype.
...
...
@@ -958,7 +942,7 @@ int Func2(void);
Use function names to describe functions, and only add function header comments if necessary.
Do not write useless or redundant function headers. Do not write empty function headers with no content.
The function header comment content is **optional** any may include the following: function description, return value, performance constraint, usage, memory convention, algorithm implementation, and reentering requirements.
The following content is **optional** in the function header comment: function description, return value, performance constraint, usage, memory convention, algorithm implementation, and reentering requirement.
In the function interface declaration in the external header file of the module, the function header comment should clearly describe important and useful information.
Example:
...
...
@@ -966,7 +950,7 @@ Example:
```c
/*
* The number of written bytes is returned. If -1 is returned, the write operation fails.
* Note that, the memory buffer is released by the caller.
* Note that the memory buffer is released by the caller.
*/
intWriteString(char*buf,intlen);
```
...
...
@@ -987,13 +971,13 @@ Problems in the preceding example are as follows:
- The 'Parameter' and 'Return value' headings have no content.
- The function name has redundant information.
-The most important thing, the notice to release the buffer, is not clearly stated.
-It does not clearly state the party that should release the buffer.
## <a name="c3-4"></a>Code Comments
### <a name="r3-3"></a>Rule 3.3 Code comments are placed above or to the right of the corresponding code.
### <a name="r3-3"></a>Rule 3.3 Place code comments above or to the right of the code.
### <a name="r3-4"></a>Rule 3.4 There must be a space between the comment character and the comment content. At least one space is required between the comment and code if the comment is placed to the right of code.
### <a name="r3-4"></a>Rule 3.4 Add a space between the comment character and the comment content, and one space between the comment and code if the comment is placed to the right of the code.
Comments placed above code should be indented to the same level as the code.
Select and use one of the following styles:
...
...
@@ -1011,7 +995,7 @@ DoSomething();
**Use '/\*' '\*/' to start the comment.**
```c
/*Single-line comment */
/*Single-line comment */
DoSomething();
/*
...
...
@@ -1049,25 +1033,12 @@ The correct method is to delete unnecessary code. If necessary, consider porting
Here, commenting out refers to the removal of code from compilation without actually deleting it. This is done by using /\*\*/, //, #if 0, #ifdef NEVER\_DEFINED, and so on.
### <a name="a3-1"></a>Rec 3.1 No TODO, TBD, or FIXME comment is allowed in code delivered to a customer.
TODO and TBD comments are used to describe required improvements and supplements.
FIXME comments are used to describe defects that need fixing.
They should have a standardized style, which facilitates text search. For example:
```c
// TODO(<author-name>): Supplement to XX
// FIXME: XX defect
```
### <a name="a3-1"></a>Rec 3.1 Provide comments if `break` or `return` is not added to the end of the `case` statement block (fall-through).
During version development, this type of comments can be used for highlighting, and must be processed and deleted before delivery.
Sometimes, the same thing is needed for multiple `case` tags. When a `case` statement ends without `break` or `return`, the statement in the next `case` tag will be executed.This is called "fall-through".
In this case, add comments for the "fall-through" to clearly express your intention, or at least explicitly specify the "fall-through".
### <a name="a3-2"></a>Rec 3.2 If 'break' or 'return' is not added to the end of the case statement block (fall-through), comments must be provided.
Sometimes, the same thing is needed for multiple case tags. When a case statement ends without 'break' or 'return', the statement in the next case tag will be executed. This is called "fall-through".
In this case, you need to add comments for the "fall-through" to clearly describe your intention.
For example, explicitly specify the "fall-through”:
For example, to explicitly specify the "fall-through":
```c
switch(var){
...
...
@@ -1084,7 +1055,7 @@ switch (var) {
}
```
If the case statement is empty, the comment explaining the "fall-through" can be omitted.
If the `case` statement is empty, no comment is required to explain the "fall-through".
```c
switch(var){
...
...
@@ -1100,26 +1071,26 @@ switch (var) {
# <a name="c4"></a>4 Header Files
**For the C language, the design of the header file reflects most of the system design.**
**For the C programming language, the design of the header file reflects most of the system design.**
The correct use of the header file makes code more readable, reduces file size, and speeds up compilation and build performance.
This chapter summarizes some methods from the perspective of programming specifications to help you properly plan header files.
The following programming specifications aim to help you properly plan header files.
## <a name="c4-1"></a>Header File Responsibility
A header file is an external interface of a module or file.
The interface declaration for most functions (except inline functions), is more suitable in the header file, than as interface implementations.
Header responsibility should be simple. A complex header file will make dependencies complex and cause long compilation times.
The interface declaration for most functions (except inline functions) is suitable in the header file. Interface implementations are not allowed in the header file.
Header responsibility should be simple. A complex header file will make dependencies complex and cause a long compilation time.
### <a name="a4-1"></a>Rec 4.1 Each .c file must have a corresponding .h file, which is used to declare the interfaces that need to be disclosed externally.
### <a name="a4-1"></a>Rec 4.1 For each .c file, provide a corresponding .h file, which is used to declare the interfaces that need to be provided externally.
Generally, each .c file has a corresponding .h file (Not necessarily the same name.), which is used to store the function declarations, macro definitions, and type definitions that are to be exposed externally.
Generally, each .c file has a corresponding .h file (not necessarily with the same name), which is used to store the function declarations, macro definitions, and type definitions that are to be exposed externally.
If a .c file does not need to open any interface externally, it should not exist.
Exception: the entry point of the program (for example, the file where the main function is located), unit test code, and dynamic library code.
Exceptions: the entry point of the program (for example, the file where the main function is located), unit test code, and dynamic library code.
Example:
foo.h content
Content of **foo.h**:
```c
#ifndef FOO_H
...
...
@@ -1130,7 +1101,7 @@ int Foo(void); // Good: Declare an external interface in the header file.
#endif
```
foo.c content
Content of **foo.c**:
```c
staticvoidBar(void);// Good: The declaration of the internal function is placed in the header of the .c file, declaring its static scope.
...
...
@@ -1146,19 +1117,19 @@ static void Bar(void)
}
```
Internally used functions declarations, macros, enumerations, structures, and others should not be placed in header files.
Internally used functions declarations, macros, enums, structs, and others should not be placed in header files.
In some products, one .c file corresponds to two .h files. One is used to store external public interfaces, and the other is used to store definitions and declarations among others for internal use to limit the number of code lines in the .c file.
This style is not recommended. It is used only because the .c file is too large. It should be split into another file instead.
This style is not recommended. It is used only because the .c file is too large. You should consider splitting the .c file first.
In addition, if private definitions and declarations are placed in independent header files, they technically cannot avoid inclusion.
This rule, in turn, is not necessarily correct. For example:
This rule, in turn, is not necessarily correct. Example:
Some simple header files, such as the command ID definition header file, do not need to have the corresponding .c file.
If a set of interface protocols has multiple instances and the interface is fixed, one .h file can have multiple .c files.
### <a name="a4-2"></a>Rec 4.2 Use .h as the extension of the header file, rather than other unconventional extensions, for example, .inc.
Some products use .inc as the header file name extension, which does not comply with the C language. A header file using .inc as the file name extension usually indicates a private header file. However, in practice, this recommendation is not followed properly. An .inc file is generally contained in multiple .c files. This document does not recommend that private definitions be stored in header files. For details, see [Rec 4.1](#a4-1).
Some products use .inc as the header file name extension, which does not comply with the C programming language.A header file using .inc as the file name extension usually indicates a private header file. However, in practice, this recommendation is not followed properly. An .inc file is generally contained in multiple .c files. It is not recommended that private definitions be stored in header files. For details, see [Rec 4.1](#a4-1).
## <a name="c4-2"></a>Header File Dependency
...
...
@@ -1168,7 +1139,7 @@ Generally, an unstable module depends on a stable module. When the unstable modu
Dependency direction is as follows: Products depend on the platform, and the platform depends on the standard library.
In addition to unstable modules depending on stable modules, each module depends on the interface. In this way, in case of any internal implementation changes to one module, users do not need to recompile another module.
This assumes that the interface itself is the most stable.
It is assumed that the interface is the most stable.
### <a name="r4-1"></a>Rule 4.1 Forbid cyclic dependency of header files.
...
...
@@ -1177,34 +1148,33 @@ For a unidirectional dependency: a.h contains b.h, b.h contains c.h, and c.h doe
The cyclic dependency of header files reflects an obviously unreasonable architecture design, which can be avoided through optimization.
### <a name="r4-2"></a>Rule 4.2 The header file must have the internal #include protection character (#define protection).
### <a name="r4-4"></a>Rule 4.2 Include the internal #include protection character (#define protection) in the header file.
To prevent header files from being included multiple times, all header files should be protected by #define. Do not use #pragma once.
When defining a protection character, comply with the following rules:
-The protection character uses a unique name. It is recommended to consider the file path and name below the top layer of the project code tree.
-Use a unique name for the protection character in the format of subsystem\_component\_file.
- Do not place code or comments before or after the protected part, except for file header comments.
Assume that the path to timer.h of the timer module is `timer/include/timer.h`. If the protection character resembles 'TIME\_H', it is not unique. Add a path, for example:
Assume that the **timer.h** file of the timer component of the util subsystem is stored in the **timer/include/timer.h** directory. If `TIME_H` is used as the protection character, name conflicts may occur. Use the following protection characters instead:
```c
#ifndef TIMER_INCLUDE_TIMER_H
#define TIMER_INCLUDE_TIMER_H
#ifndef UTIL_TIMER_TIMER_H
#define UTIL_TIMER_TIMER_H
...
#endif
#endif // UTIL_TIMER_TIMER_H
```
### <a name="r4-3"></a>Rule 4.3 Do not reference external function interfaces and variables by using declaration.
### <a name="r4-5"></a>Rule 4.3 Do not reference external function interfaces and variables by using declaration.
You can use the interfaces provided by other modules or files only by using header files.
Using external function interfaces and variables with an extern declaration may cause inconsistency between declarations and definitions when external interfaces are changed.
In addition, this kind of implicit dependency may cause architecture corruption.
Cases that do not comply with specifications:
a.c content
Content of **a.c**:
```c
externintFoo(void);// Bad: Reference external functions by using the extern declaration.
...
...
@@ -1216,10 +1186,10 @@ void Bar(void)
```
It should be changed to:
a.c content
Content of **a.c**:
```c
#include "b.h" // Good: Use the interface providing the interface.
#include "b.h" // Good: Use the interfaces provided by another .c file by including the header file.
voidBar(void)
{
inti=Foo();
...
...
@@ -1227,13 +1197,13 @@ void Bar(void)
}
```
b.h content
Content of **b.h**:
```c
intFoo(void);
```
b.c content
Content of **b.c**:
```c
intFoo(void)
...
...
@@ -1243,18 +1213,18 @@ int Foo(void)
```
In some scenarios, if internal functions need to be referenced with no intrusion to the code, the extern declaration mode can be used.
For example:
Example:
When performing unit testing on an internal function, you can use the extern declaration to reference the tested function.
When a function needs to be stubbed or patched, the function can be declared using extern.
### <a name="r4-4"></a>Rule 4.4 Do not include header files in extern "C".
### <a name="r4-6"></a>Rule 4.4 Do not include header files in extern "C".
If a header file is included in extern "C", extern "C" may be nested. Some compilers restrict the nesting of extern "C". If there are too many nested layers, compilation errors may occur.
If a header file is included in extern "C", extern "C" may be nested. Some compilers restrict the number of nesting layers of extern "C". Too many nesting layers may cause compilation errors.
extern "C" usually occurs in mixed programming using both C and C++. If the extern "C" includes a header file, the original intent behind the header file may be hindered. For example, when the linkage specifications are modified incorrectly.
extern "C" usually occurs in mixed programming using both C and C++. If extern "C" includes a header file, the original intent behind the header file may be hindered, for example, when linkage specifications are changed incorrectly.
For example, assume that there are two header files a.h and b.h.
a.h content
Assume that there are two header files: **a.h** and **b.h**.
Content of **a.h**:
```c
...
...
...
@@ -1266,7 +1236,7 @@ void A(int)
#endif
```
b.h content
Content of **b.h**:
```c
...
...
...
@@ -1282,7 +1252,7 @@ void B(void);
#endif
```
Using the C++ preprocessor to expand b.h, the following information is displayed:
When you use the C++ preprocessor to expand **b.h**, the following information is displayed:
```c
extern"C"{
...
...
@@ -1291,42 +1261,42 @@ extern "C" {
}
```
In the a.h file, the function Foo is intended to be a C++ free function following the C++ specifications. However, in the b.h file, because `#include "a.h"` is placed inside `extern "C"`, the linking specification of function Foo is changed incorrectly.
In the **a.h** file, the **Foo** function is intended to be a C++ free function following the C++ specifications. However, in the **b.h** file, because `#include "a.h"` is placed inside `extern "C"`, the linking specification of the **Foo** function is changed incorrectly.
Exception: In the C++ compilation environment, if you want to reference a header file written in pure C, a non-intrusive approach is to exclude the C header file from `extern "C"`.
Exceptions: In the C++ compilation environment, if you want to reference a header file written in pure C, a non-intrusive approach is to exclude the C header file from `extern "C"`.
# <a name="c5"></a>5 Functions
Functions help avoid repeated code and increase reusability. In addition, functions act to layer code and reduce code complexity, hiding implementation details, making programs more modular, and facilitating reading and maintenance.
Functions help avoid repeated code and increase reusability. Functions are layered to reduce complexity and hide implementation details, making programs more modular and facilitating code reading and maintenance.
The function should be concise and short.
Functions should be concise and short.
One function completes only one thing.
## <a name="c5-1"></a>Function Design
Writing clean functions and organizing code effectively is the essence of good function design. The code should be simple and not conceal the designer's intention, using clean abstractions and straightforward control statements to organize the function naturally.
The essence of function design is to write clean functions and organize code effectively.The code should be simple and not conceal the designer's intention, using clean abstractions and straightforward control statements to organize the function naturally.
### <a name="r5-1"></a>Rule 5.1 Avoid long functions and ensure that functions contain no more than 50 lines (not including blank lines and comments).
### <a name="r5-1"></a>Rule 5.1 Avoid long functions and ensure that functions contain no more than 50 lines (excluding blank lines and comments).
A function should fit on one screen, (be no longer than 50 lines), do only one thing, and do it well.
A function should be able to be displayed on one screen (fewer than 50 lines). It does only one thing and does it well.
Large functions are often caused by the fulfillment of more than one purpose by the function, or over complication when parts could be abstracted.
A long function usually means that it aims to implement complex functionalities or contains excess details.
Exception:
Exceptions:
Considering the code's aggregation and functionality, some functions may exceed 50 lines, but only if the code is readable and concise.
These exceptions should be minimal, such as specific algorithm processing.
Even if a large function works well in the moment, once someone modifies it, new problems may occur. It may even cause bugs that are difficult to discover.
It is recommended that you split it into several simpler and easier to manage functions, facilitating reading and modification of code.
It is recommended that you split the code into several functions that are simpler and easier to manage so that others can easily read and modify the code.
### <a name="r5-2"></a>Rule 5.2 Avoid nesting a code block more than four times within a function.
The nested code block depth of a function refers to the layered depth of a code control block (created by statements such as: if, for, while, and switch).
Each layer of nesting increases the brain power required to read the code, because you need to maintain a "stack" in your mind comprised of each conditional statement, loop, and so on.
Code should be broken down functionally to prevent the reader from remembering too much context at a time.
The nested code block depth of a function refers to the layered depth of a code control block (created by statements such as`if`, `for`, `while`, and `switch`).
Each layer of nesting increases the difficulty in reading the code.
Further functional decomposition should be done for better understanding.
Using `guardian statements`, (a short conditional return), can effectively reduce the nesting layers of if statements. For example:
The original code nesting level is 3:
Using `guard clauses` can effectively reduce the nesting layers of the `if` statements. Example:
Three nesting layers are used originally:
```c
intFoo(...)
...
...
@@ -1341,7 +1311,7 @@ int Foo(...)
}
```
Refactored code using the `guardian statement`, the nesting level is 2:
Two nesting layers after code reconstruction using `guard clauses`:
```c
intFoo(...)
...
...
@@ -1359,8 +1329,8 @@ int Foo(...)
}
```
Exception:
Considering the code's aggregation and functionality, some functions may exceed 4 times nested, but only if the code is readable and concise.
Exceptions:
Considering the code's aggregation and functionality, some functions may contain 4 or more nesting layers, but only if the code is readable and concise.
These exceptions should be rare.
### <a name="a5-1"></a>Rec 5.1 Process all returned error codes.
...
...
@@ -1371,7 +1341,7 @@ Example:
```c
charfileHead[128];
ReadFileHead(fileName,fileHead,sizeof(fileHead));// Bad: The returned value is not checked.
ReadFileHead(fileName,fileHead,sizeof(fileHead));// Bad: The return value is not checked.
DealWithFileHead(fileHead,sizeof(fileHead));// The 'fileHead' is possibly invalid.
```
...
...
@@ -1388,7 +1358,7 @@ if (ret != OK) { // Good: Ensure that the 'fileHead' is written.
DealWithFileHead(fileHead,sizeof(fileHead));// Process the file header.
```
Note that when the return value of a function is ignored and instead void is returned frequently, consider whether the return value of the function is designed reasonably.
If the return value of a function is ignored and `void` is returned frequently, check whether the return value of the function is designed properly.
Only if the caller of a function really doesn't need a return value, should you design the function to return `void`.
## <a name="c5-2"></a>Function Parameters
...
...
@@ -1397,14 +1367,26 @@ Only if the caller of a function really doesn't need a return value, should you
Using return values rather than output parameters improves readability and usually provides the same or better performance.
Readability can be improved by naming functions such as GetXxx, FindXxx, or directly using a single noun, to directly return the corresponding object.
Readability can be improved by naming functions such as GetXxx, FindXxx, IsXxx, OnXxx, or directly using a single noun, to directly return the corresponding object.
Exceptions:
1. When multiple values are returned, you can design an output parameter for the function.
2. If memory allocation is involved, you can design an output parameter. The caller passes the allocated memory as an output parameter, and memory allocation is not performed in the function.
### <a name="a5-3"></a>Rec 5.3 Define function parameters in the sequence of input, output, and input/output parameters.
You are advised to define function parameters in the sequence of input, output, and input/output parameters.
### <a name="r5-3"></a>Rule 5.3 Provide a release function if allocation of resources, such as memory, locks, and queues, is involved.
Resources should be released from where they are applied for. If a function applies for resources, the module must provide resource functions.
### <a name="a5-3"></a>Rec 5.3 Use strongly typed parameters and avoid using void\*
### <a name="a5-4"></a>Rec 5.4 Use strongly typed parameters. Do not use void\*.
While different languages have their own views on strong typing and weak typing, it is generally believed that C/C++ is a strongly typed language. Since we use such a strongly typed language, we should keep this style.
The advantage of this strongly typed style is to prevent evasive errors by catching errors at the compilation stage.
Strong types help the compiler find more errors for us. Pay attention to the usage of the `FooListAddNode` function in the following code:
Strong types help the compiler find more errors.Pay attention to the usage of the `FooListAddNode` function in the following code:
```c
structFooNode{
...
...
@@ -1429,7 +1411,7 @@ void MakeTheList(...)
BarNode*bar;
...
FooListAddNode(bar);// Wrong: In this example, the foo parameter was supposed to be passed, but the bar parameter is incorrectly passed instead. No error is reported immediately and issues may occur as a result.
FooListAddNode(bar);// Wrong: In this example, the bar parameter rather than the foo parameter is passed.No error is reported immediately and issues may occur as a result.
Exception: For some generic interfaces, you can use the input parameter `void *` to pass different types of pointers.
Exceptions: For some generic interfaces, you can use the input parameter `void *` to pass different types of pointers.
### <a name="a5-4"></a>It is the caller's responsibility to check the validity of internal function parameters of a module.
### <a name="a5-5"></a>Rec 5.5 It is the caller's responsibility to check the validity of internal function parameters of a module.
Validity checks must be performed on data received from external modules to protect programs against illegal input.
Validity check must be performed on parameters passed from external modules to protect programs from being damaged by invalid input data.
When calling internal functions, by default, the caller is responsible for ensuring the validity of any returned data. If the callee takes responsibility for checking data validity, checks may be performed multiple times and redundant code is generated. This is not concise.
The caller ensures the validity of any received data. This type of contractual programming can make logic simpler and code more readable.
When the caller ensures the validity of any received data, this contractual programming makes logic simpler and code more readable.
Example:
```c
...
...
@@ -1460,7 +1442,7 @@ int SomeProc(...)
intdata;
booldataOK=GetData(&data);// Get data.
if(!dataOK){// Check the result of the previous step ensuring data validity.
if(!dataOK){// Check the result of the previous step to ensure data validity.
return-1;
}
...
...
@@ -1470,7 +1452,7 @@ int SomeProc(...)
voidDealWithData(intdata)
{
if(data<MIN||data>MAX){// Bad: The caller has already ensured the validity of the data.
if(data<MIN||data>MAX){// Bad: The caller has already ensured data validity.
return;
}
...
...
@@ -1478,7 +1460,7 @@ void DealWithData(int data)
}
```
### <a name="a5-5"></a>Rec 5.5 The pointer argument of a function should be declared as 'const' if it is not used to modify the pointed object.
### <a name="a5-5"></a>Rec 5.5 Declare the pointer argument of a function as 'const' if it is not used to modify the pointed object.
The const pointer argument, which restricts the function from modifying the object through the pointer, makes code stronger and safer.
...
...
@@ -1488,9 +1470,9 @@ Example: In the example of the strncmp in the 7.21.4.4 of the C99 standard, the
intstrncmp(constchar*s1,constchar*s2,size_tn);// Good: The invariant parameter is declared as const.
```
Note: Whether or not the pointer parameter is to be declared as 'const' depends on the function design, not on whether there is a "modify object" action in the function entity.
Note: Whether to declare the pointer parameter as `const` depends on the function design, but not on whether there is a "modify object" action in the function entity.
### <a name="a5-6"></a>Rec 5.6 Ensure that the number of parameters in a function is less than or equal to 5.
### <a name="a5-6"></a>Rec 5.6 Include no more than 5 parameters in a function.
If a function has too many parameters, the function is easy to be affected by changes in external code, hindering maintenance. Too many function parameters will also increases the workload for testing.
...
...
@@ -1503,18 +1485,18 @@ The number of parameters in a function must not exceed 5. If the number of param
An inline function is a function optimization method introduced by C99. Function inlining can eliminate the overhead of function calls; thanks to inlining, combination with the called code is implemented, so that the compiler can achieve further code optimization from a larger perspective. The inline function is similar to a function-like macro. For details, see [Rec 6.1](#a6-1).
### <a name="a5-7"></a>Rec 5.7 Ensure that the inline function contains no more than 10 lines (non-blank and non-comment).
### <a name="a5-7"></a>Rec 5.7 Include no more than 10 lines in an inline function (excluding blank lines and comments).
Defining a function as an inline function generally aims to improve performance, but it does not always improve performance. If the function body is short, function inlining can effectively reduce the size of the target code and improve the function execution efficiency.
Defining a function as an inline function generally aims to improve performance, though it may fail to do so.If the function body is short, function inlining can effectively reduce the size of the target code and improve the function execution efficiency.
Vice versa, if the function body is large, inlining will cause expansion of the target code, especially when there are many call points.
It is recommended that inline functions be controlled to within **10** lines.
Do not abuse inline functions to improve performance. Avoid premature optimization. In general, a function can be defined as an inline function only when actual test data proves that the inlining achieves higher performance. Functions such as setter and getter functions, that are short and called frequently can be defined as inline functions.
Do not abuse inline functions to improve performance. Avoid premature optimization. In general, a function can be defined as an inline function only when actual test data proves that the inlining achieves higher performance. Functions such as setter and getter functions, which are short and called frequently, can be defined as inline functions.
### <a name="r5-3"></a>Rule 5.3 Define inline functions that will be called by multiple source files in the header file.
Inline functions are unfolded in compilation. Therefore, the inline function definition must be visible in each source file that calls this function.
As shown in the following code, inline.h has only the declaration of the `SomeInlineFunc` function but no definition. The other.c file includes inline.h. As a result, inlining fails when `SomeInlineFunc` is called.
As shown in the following code, **inline.h** contains the declaration of the `SomeInlineFunc` function but not the definition. The **other.c** file includes **inline.h**. As a result, inlining fails when `SomeInlineFunc` is called.
inline.h
...
...
@@ -1541,7 +1523,7 @@ int OtherFunc(void)
}
```
Due to this restriction, if multiple source files need to call the same inline function, the definition of the inline function needs to be placed in the header file.
Due to this restriction, if multiple source files need to call the same inline function, the definition of the inline function must be placed in the header file.
The inline function implementation in **gnu89** differs from that in the **C99** standard. For compatibility, you can declare the function as **static inline**.
# <a name="c6"></a>6 Macros
...
...
@@ -1560,17 +1542,17 @@ A function-like macro is a macro (as shown in the following example) similar to
} while (0)
```
### <a name="a6-1"></a>Use functions instead of function-like macros.
### <a name="a6-1"></a>Rec 6.1 Use functions instead of function-like macros.
Before defining a function-like macro, consider whether it can be replaced with a function. If so, you are advised to replace macros with functions.
Before defining a function-like macro, consider whether it can be replaced with a function. If yes, you are advised to use a function for replacement.
The disadvantages of the function-like macro are as follows:
- Function-like macros haves no type check, which is not as strict as the function call check. For the example code, see [Below](#macro_lack_of_type_check__example).
- If macro parameters are not calculated during macro expansion, unexpected results may be generated. For details, see [Rule 6.1](#r6-1) and [Rule 6.3](#r6-3).
-The macro has no independent scope. When it is used together with control flow statements, unexpected results described in [Rule 6.2](#r6-2) may be generated.
- There are high skill requirements on the proper use of macros (see the following rules), for example, the usage of `#` and parentheses affects readability.
-A macro has no independent scope. When it is used together with control flow statements, unexpected results described in [Rule 6.2](#r6-2) may be generated.
- There are high skill requirements on the proper use of macros (for example, the usage of `#` and wide use of parentheses), which reduces readability.
- Extensions of some macros can only be implemented by specific compilers in specific scenarios, such as `statement expression` of `gcc`, reducing the portability.
- After the macro is expanded at the pre-compilation stage, it is invisible during subsequent compilation, linking, and debugging. Macros that contain multiple lines are expanded into a line. Function-like macros are difficult to debug, interrupt, and locate in the case of bugs.
- After the macro is expanded during precompilation, it is invisible during subsequent compilation, linking, and debugging. Besides, macros that contain multiple lines are expanded into a line.
- Macros containing a large number of statements must be expanded at each call point. If there are many call points, the code will be expanded.
<aname="macro_lack_of_type_check__example"></a>Example code of a function-like macro lacking type check:
...
...
@@ -1601,19 +1583,19 @@ MACRO: max of a(1) and b(-1) is -1
FUNC : max of a(1) and b(-1) is 1
```
The function does not have the preceding macro disadvantages. However, compared with macros, the biggest disadvantage is that the execution efficiency is not as high (increasing the overhead of function calls and the difficulty of compiler optimization).
The function does not have the preceding macro disadvantages. However, compared with macros, the biggest disadvantage of functions is its low execution efficiency (increasing the overhead of function calls and the difficulty of compiler optimization).
Therefore, the C99 standard introduces inline functions (gcc introduces inline functions ahead of this standard).
The inline function is similar to the macro, as it is also expanded at the call point. The difference is that inline functions are expanded during compilation.
Inline functions have the advantages of both functions and macros:
- Strict type checking is performed for inline functions and functions.
-The parameter of an inline function/function is calculated only once.
-Each input parameter of an inline function or function is calculated only once.
- Inline functions are unfolded in place and there is no overhead for function calls.
- Inline functions are better optimized than standard functions.
For performance-sensitive code, consider using inline functions instead of function-like macros.
Functions and inline functions cannot completely replace function-like macros, because function-like macros are more suitable for certain scenarios.
Functions and inline functions cannot completely replace function-like macros, since function-like macros are more suitable for certain scenarios.
For example, in a log scenario, using a function-like macro with variable parameters and default parameters is more convenient.
```c
...
...
@@ -1621,7 +1603,7 @@ int ErrLog(const char *file, unsigned long line, const char *fmt, ...);
### <a name="r6-1"></a>Rule 6.1 When a macro is defined, macro parameters must use complete parentheses.
### <a name="r6-1"></a>Rule 6.1 Use complete parentheses for macro parameters when defining a macro.
The macro parameter is replaced by text only when the macro is expanded. The value is calculated when the macro is compiled. After the text replacement, the statements contained in the macro are combined with called code.
The expression after combination may result in a different result than expected, especially when the macro parameter is in an expression.
...
...
@@ -1629,7 +1611,7 @@ The expression after combination may result in a different result than expected,
The following is an incorrect format:
```c
#define SUM(a, b) a + b // Bad.
#define SUM(a, b) a + b // Bad
```
When the macro is used, the execution result is inconsistent with the expected result.
...
...
@@ -1637,7 +1619,7 @@ When the macro is used, the execution result is inconsistent with the expected r
This problem can be solved by adding parentheses to the entire expression, as shown in the following:
```c
#define SUM(a, b) (a + b) // Bad.
#define SUM(a, b) (a + b) // Bad
```
However, this method has the following problems:
...
...
@@ -1646,7 +1628,7 @@ However, this method has the following problems:
To solve this problem, add parentheses to each macro parameter, as shown in the following:
```c
#define SUM(a, b) (a) + (b) // Bad.
#define SUM(a, b) (a) + (b) // Bad
```
The third scenario is as follows: `SUM(2, 8) * 10` . The result after the extension is `(2) + ((8) * 10)`, which is inconsistent with the expected result `(2 + 8) * 10`.
...
...
@@ -1657,7 +1639,7 @@ In conclusion, the correct format is as follows:
#define SUM(a, b) ((a) + (b)) // Good.
```
But avoid abusing parentheses. As shown in the following, adding parentheses to a single identifier or a positive number is meaningless.
Avoid abusing parentheses. As shown in the following, adding parentheses to a single identifier or a positive number is meaningless.
```c
#define SOME_CONST 100 // Good: No parentheses needed for a single positive number.
...
...
@@ -1668,8 +1650,8 @@ But avoid abusing parentheses. As shown in the following, adding parentheses to
Notes:
-Macro parameters in '#', '##' operation do not need parentheses.
-Macro parameters which participating in string splicing do not need parentheses.
-Do not add parentheses to macro parameters when they are involved in the '#' or '##' operation.
-Do not add parentheses to macro parameters when they are used for string concatenation.
- If a macro parameter is used as a separate part in one side of an assignment expression (including +=, -=, etc.), parentheses are not required.
- If a macro parameter is used as a separate part in comma expressions, functions or macro call lists, parentheses are not required.
...
...
@@ -1680,25 +1662,25 @@ Example:
#define HELLO_STR(obj) "Hello, " obj // No parentheses for 'obj'
#define ADD_3(sum, a, b, c) (sum = (a) + (b) + (c)) // 'a', 'b', and 'c' need parentheses, while 'sum' does not.
#define ADD_3(sum, a, b, c) (sum = (a) + (b) + (c)) // 'a', 'b', and 'c' need parentheses, whereas 'sum' does not.
#define FOO(a, b) Bar((a) + 1, b) // 'a' needs parentheses, while 'b' does not.
#define FOO(a, b) Bar((a) + 1, b) // 'a' needs parentheses, whereas 'b' does not.
```
### <a name="r6-2"></a>Rule 6.2 Implementation statements of function-like macros that contain multiple statements must be placed in a do-while(0).
### <a name="r6-2"></a>Rule 6.2 Place implementation statements of function-like macros that contain multiple statements in a `do-while(0)`.
Macros do not have code blocks. Macros do not have code blocks. When a macro is expanded at the call point, the expressions and variables defined in the macro are integrated into the calling code. As a result, variable name conflict and segmentation of macro statements may occur. Use do-while(0) to add a boundary to the macro so that the macro has an independent scope. In addition, a single statement can be formed by combining the macro with a semicolon (;) to avoid this problem.
Macros do not have code blocks. When a macro is expanded at the call point, the expressions and variables defined in the macro are integrated into the calling code. As a result, variable name conflict and segmentation of macro statements may occur. Use `do-while(0)` to add a boundary to the macro so that the macro has an independent scope. In addition, a single statement can be formed by combining the macro with a semicolon (;) to avoid this problem.
The following macro is incorrect:
Incorrect example:
```c
// Not Good.
// Not Good
#define FOO(x) \
(void)printf("arg is %d\n", (x)); \
DoSomething((x));
```
When the macro is called as shown in the following example code, the for loop only executes the first statement of the macro, and the next statement of the macro is executed only after the loop ends.
When the macro is called as shown in the following example code, the `for` loop only executes the first statement of the macro, and the next statement of the macro is executed only after the loop ends.
```c
for(i=1;i<10;i++)
...
...
@@ -1724,21 +1706,21 @@ else
FOO(20);
```
The correct format is to wrap the executed body using a do-while(0), as shown in the following:
The correct format is to wrap the executed body using a `do-while(0)`, as shown in the following:
```c
// Good.
// Good
#define FOO(x) do { \
(void)printf("arg is %d\n", (x)); \
DoSomething((x)); \
} while (0)
```
Exception:
Exceptions:
- Macros that contain 'break' or 'continue' statements can be treated as exceptions. Do use these macros carefully.
- An exception can be made when the macro contains an incomplete statement. For example, use a macro to encapsulate the conditional part of the for loop.
- An exception can be a non-multiple statement, or a single if/for/while/switch statement.
- An exception can be made when the macro contains an incomplete statement. For example, use a macro to encapsulate the conditional part of the `for` loop.
- An exception can be a non-multiple statement, or a single `if`, `for`, `while`, or `switch` statement.
### <a name="r6-3"></a>Rule 6.3 Do not pass expressions with side effects to a function-like macro.
...
...
@@ -1765,11 +1747,11 @@ a++; // Result: a = 6, which is added only once.
In addition, if the macro parameter contains a function call, the function may be called repeatedly after the macro is expanded.
If the function execution results are the same, it is a waste; if the results are different, the execution result may not meet the expected value.
### <a name="a6-2"></a>Rec 6.2 Exercise caution when you use the statements such as return, goto, continue, and break in a function-like macro definition.
### <a name="a6-2"></a>Rec 6.2 Exercise caution when you use the statements such as `return`, `goto`, `continue`, and `break` in a function-like macro definition.
In a macro, the process changing statements, such as return, goto, continue, and break. While they can simplify the code, they hide the real process, which hinders understanding and causes resource leakage.
Although process changing statements, such as `return`, `goto`, `continue`, and `break`, in a macro can simplify the code, they hide the real process, which hinders understanding and easily causes resource leakage.
First, the macro encapsulation of the statement 'return' can easily lead to excessive encapsulation and use.
First, the macro encapsulation of the `return` statement can easily lead to excessive encapsulation and use.
As shown in the following code, the judgment of `status` is a part of the main process. After being encapsulated in macros, the purpose is not intuitive. The `RETURN_IF` macro is ignored, causing a confused understanding.
```c
...
...
@@ -1787,15 +1769,15 @@ As shown in the following code, the judgment of `status` is a part of the main p
} while (0)
ret=InitModuleA(a,b,&status);
LOG_AND_RETURN_IF_FAIL(ret,"Init module A failed!");// OK.
LOG_AND_RETURN_IF_FAIL(ret,"Init module A failed!");// OK
RETURN_IF(status!=READY,ERR_NOT_READY);// Bad: The most important logic is not obvious.
ret=InitModuleB(c);
LOG_AND_RETURN_IF_FAIL(ret,"Init module B failed!");// OK.
LOG_AND_RETURN_IF_FAIL(ret,"Init module B failed!");// OK
```
Second, if return is encapsulated in a macro, it may also cause a memory leak. Example is as follows:
Second, if `return` is encapsulated in a macro, it may also cause a memory leak. Example:
If `mem2` fails to apply for memory, the `CHECK_PTR` will return a message instead of releasing `mem1`.
If `mem2` fails to apply for memory, `CHECK_PTR` will return a message instead of releasing `mem1`.
Besides, the name of the `CHECK_PTR` macro is not good. The macro name only reflects the check action and does not specify the result. Readers can see that a failure is returned when the pointer is null only after viewing the macro implementation. It's not inherently obvious.
In summary: It is not recommended to encapsulate statements that change control flow, such as return, goto, continue, and break in macro definitions.
However, these macros can be used such as return value judgment as an exception.
In summary, it is not recommended to encapsulate process changing statements, such as `return`, `goto`, `continue`, and `break`, in macro definitions.
However, these macros can be used in special scenarios, such as return value judgment.
Note: **Macro names must contain descriptive keywords if statements that will change control flow such as return, goto, continue, and break are used.**
Note: **Macro names must contain descriptive keywords if process changing statements, such as `return`, `goto`, `continue`, and `break`, are used.**
### <a name="a6-3"></a>Rec 6.3 Ensure that function-like macros contain no more than 10 lines (not including blank lines and comments).
### <a name="a6-3"></a>Rec 6.3 Include no more than 10 lines in a function-like macro (excluding blank lines and comments).
A big problem of the function-like macro is that it is more difficult to debug and locate than the function, especially when the macro is too long.
A function-like macro is more difficult to debug and locate than a function, especially when the macro is too long.
Macro expansion will also lead to more compiled code. It is recommended that function-like macros contain no more than 10 lines.
# <a name="c7"></a>7 Variables
...
...
@@ -1887,7 +1869,7 @@ For example, the PCLint tool reports a warning for the following two examples.
### <a name="r7-3"></a>Rule 7.3 Forbid invalid and redundant variable initialization.
If the initial value is not determined before initialization is performed, it is not concise but not secure, which may introduce problems that are more difficult to discover.
If the initial value is not determined before initialization is performed, it is not concise or secure and may cause problems that are more difficult to discover.
Common redundant initialization:
...
...
@@ -1940,7 +1922,7 @@ The C99 does not limit the definition position of local variables to before the
This concise approach not only limits the scope of the variable scope, but also solves the problem of how to initialize the variable when it is defined.
If this compilation environment is supported, you are advised to define local variables in this way.
**Exception:**
**Exceptions:**
**As 'Secure Coding Standard' required, pointers, resource variables, and boolean variables can be treated as exceptions of this rule.**
### <a name="r7-4"></a>Rule 7.4 Do not use magic numbers.
...
...
@@ -1978,9 +1960,9 @@ There are special cases: for example, the expression `if (MIN < v && v < MAX)` i
You do not need to worry about accidentally writing '==' as '=' because a compilation alarm will be generated for `if (v = MAX)` and an error will be reported by other static check tools. Use these tools to solve such writing errors and ensure that that code is readable.
### <a name="r8-1"></a>A full expression containing an increment (++) or decrement (--) operator should have no other using of the variable.
### <a name="r8-1"></a>Do not reference a variable again in an expression containing an increment (++) or decrement (--) operator.
In an expression containing a variable increment or decrement operation, if the variable is referenced again, the result is not explicitly defined in the C standard, which may vary between compilers or different versions of the same compiler.
In an expression containing a variable increment or decrement operator, if the variable is referenced again, the result is not explicitly defined in the C standard, which may vary between compilers or different compiler versions.
For better portability, you should not make any assumptions about the operation sequence not defined in any standards.
Note that this problem cannot be solved by using parentheses because it is not a problem of priority.
...
...
@@ -2024,12 +2006,12 @@ c = (a & 0xFF) + b; /* Parentheses are required while using bit operators. *
## <a name="c8-2"></a>Statements
### <a name="r8-2"></a>Rule 8.2 The switch statement must have a 'default' branch.
### <a name="r8-2"></a>Rule 8.2 Provide a 'default' branch for the `switch` statement.
In most cases, the 'default' branch exists in the switch statement to ensure that there is a default processing action when the case tag is missing.
Exception:
If the switch condition variable is of the enumeration type and the case branches cover all values, then the default branch is redundant.
Exceptions:
If the switch condition variable is of the enum type and the case branches cover all values, then the default branch is redundant.
A modern compiler can check whether the case branch of some enumerated values is missing in the switch statement. A warning will be displayed.
```c
...
...
@@ -2050,11 +2032,11 @@ switch (color) {
}
```
### <a name="a8-3"></a>Rec 8.3 Exercise caution when using the goto statement.
### <a name="a8-3"></a>Rec 8.3 Exercise caution when using the `goto` statement.
The goto statement destroys the structure of the program, so you'd better not use the goto statement unless you really need it. You can only jump to statements following the goto statement, and only within the one function.
The `goto` statement destroys the program. Avoid using it if possible. You can only jump to statements following the `goto` statement, and only within the one function.
The goto statement is used to implement function return to a single point within a function.
The `goto` statement is used to implement function return to a single point within a function.
If a function has a large number of identical logics that cannot be encapsulated, for example, repeated file execution, the processed part of code after the file operation failure (for example, closing the file handle and releasing memory that is dynamically applied for) is usually placed in the last part of the function body. And the goto statement is placed right before these. In this way, the code becomes clear and concise. It can also be encapsulated in functions or macros, but doing so makes code less straightforward.
Rules are not perfect. Disabling useful features in specific situations may affect code implementation. However, the rules are formulated "to help most programmers to get more benefits". If a rule is found unhelpful or difficult to follow in team coding, please send your feedback to us so we can improve the rule accordingly.
Before referring to this guide, you are expected to have the following basic capabilities for C++. It is not for a beginner that wants to learn about C++.
## <a name="c0-1"></a>Purpose
Rules are not perfect. They might disable useful features in specific situations and therefore affect code implementation. However, the purpose of developing rules is to get more benefits for most programmers. If a rule cannot be followed in your team operation, we can improve the rule together.
Before referring to this coding style guide, you are expected to have the following basic capabilities of the C++programming language:
1. Have a general knowledge of ISO standards for C++.
2. Be familiar with the basic features of C++, including those of C++ 03/11/14/17.
3. Have a general knowledge of the C++ Standard Library.
3. Have a general knowledge of the C++ standard library.
## <a name="c0-2"></a>General Principles
Code must meet the requirements for readability, maintainability, security, reliability, testability, efficiency, and portability while ensuring functionality correctness.
## <a name="c0-3"></a>Key Points
## <a name="c0-3"></a>Key Points
1. C++ programming style, such as naming and typesetting.
2. C++ modular design, including how to design header files, classes, interfaces, and functions.
3. Best practices of C++ features, including constants, type casting, resource management, and templates.
4. Best practices of modern C++, including conventions that can improve code maintainability and reliability in C++ 11/14/17.
5. This coding style guide is preferentially applicable to C++17.
## <a name="c0-4"></a>Conventions
**Rule**: Conventions that must be followed during programming.
## <a name="c0-4"></a> Conventions
**Rule**: a regulating principle that must be followed during programming.
**Rec**: Conventions that must be considered during programming.
**Recommendation**: a guideline that must be considered during programming.
This document is applicable to standard C++ versions (C++ 03/11/14/17) unless otherwise specified.
This document is applicable to standard C++ versions (C++ 03/11/14/17) unless otherwise specified in the rule.
## <a name="c0-5"></a> Exceptions
It is necessary to understand the reason for each rule or recommendation and to try and comply with them.
## <a name="c0-5"></a>Exceptions
It is necessary to understand the reason for these conventions and try to comply with them, no matter if they are rules or recommendations.
However, some rules and recommendations have exceptions.
The only acceptable exceptions are those that do not violate the general principles and provide appropriate reasons for the exception.
Try to avoid exceptions because they affect the code consistency. Exceptions to 'Rules' should be very rare.
The style consistency principle is preferred in the following case:
When you modify external open source or third-party code, the existing code specifications prevail.
When you modify open-source or third-party code, comply with their respective code specifications.
# <a name="c2"></a>2 Naming
## <a name="c2-1"></a>General Naming Rules
__CamelCase__
CamelCase is the practice of writing compound words or phrases so that each word or abbreviation in the phrase begins with a capital letter, with no intervening spaces or punctuation.
CamelCase is the practice of writing compound words or phrases so that each word or abbreviation in the phrase begins with a capital letter, and with no intervening spaces or punctuation.
There are two conventions: UpperCamelCase and lowerCamelCase.
| Class Type, Struct Type, Enumeration Type, and Union Type Definitions, Scope Name | UpperCamelCase |
| Functions (Including Global Functions, Scope Functions, and Member Functions) | UpperCamelCase |
| Global Variables (Including Variables of the Global and Namespace Scopes, Namespace Variables, and Class Static Variables), Local Variables, Function Parameters, and Class, Struct, and Union Member Variables | lowerCamelCase |
| Macro, Constant, Enumerated Value, goto Tag | All caps, separated with underscores (`_`) |
| unctions (including global functions, scope functions, and member functions) | UpperCamelCase |
| Global variables (including variables of the global and namespace scopes, and class static variables), local variables, function parameters, and class, struct, and union member variables | lowerCamelCase |
| Macro, constant, enumerated value, goto tag| All capitalized, separated by underscores (\_)|
Note:
**Constant** indicates the variables of the basic, enumeration, or character string type modified by const or constexpr in the global scope, the namespace scope, and the scope of a static member of a class.
**Constant** in the above table refers to the variable that is of the basic data type, enumeration type, and string type and modified by **const** or **constexpr** under the global scope, the namespace scope, and the scope of a static member of a class, excluding arrays and other variables.
**Variable** indicates the variables excluding those defined in **Constant**. These variables use the lowerCamelCase style.
## <a name="c2-2"></a> File Names
### <a name="a2-2-1"></a> Recommendation 2.2.1 Use .cpp as the C++ file name extension and .h as the header file name extension.
It is recommended that you use .h as the name extension of a header file so that the header file can be directly compatible with C and C++.
It is recommended that you use .cpp as the name extension of an implementation file. In this way, you can directly distinguish C++ code from C code.
## <a name="c2-2"></a>File Naming
### <a name="r2-2-1"></a>Rule 2.2.1 Use .cpp as the C++ file name extension and .h as the header file name extension.
It is recommended that you use .h as the file name extension of a header file so that the header file is compatible with C and C++.
It is recommended that you use .cpp as the file name extension of an implementation file. In this way, you can easily distinguish C++ code from C code.
At present, there are some other file name extensions used by programmers:
...
...
@@ -66,8 +66,8 @@ If your project team uses a specific file name extension, you can continue to us
This document uses .h and .cpp extensions.
### <a name="a2-2-2"></a>Recommendation 2.2.2 Keep C++ file names the same as the class name.
The names of the C++ header file and the C++ implementation file must be the same as the class name. Use the CamelCase or Kernel style and keep the style consistent.
### <a name="r2-2-2"></a>Rule 2.2.2 Keep C++ file names the same as the class name.
The names of the C++ header file and the C++ implementation file must be the same as the class name. Use the unix\_like style and keep the style consistent.
For example, if there is a class named DatabaseConnection, the corresponding file names are as follows:
- database_connection.h
...
...
@@ -75,8 +75,8 @@ For example, if there is a class named DatabaseConnection, the corresponding fil
The naming rules of struct, namespace, and enumeration definition files are similar to the rules above.
## <a name="c2-3"></a> Function Names
Functions are named in UpperCamelCase. Generally, the verb or verb-object structure is used.
## <a name="c2-3"></a>Function Naming
Functions are named in the UpperCamelCase style. Generally, the verb or verb-object structure is used.
```cpp
classList{
public:
...
...
@@ -90,12 +90,12 @@ namespace Utils {
}
```
## <a name="c2-4"></a> Type Names
## <a name="c2-4"></a>Type Naming
Types are named in the UpperCamelCase style.
All types, such as classes, structs, unions, typedefs, and enumerations, use the same conventions. For example:
All types, such as classes, structs, unions, typedefs, and enumerations, use the same conventions. Example:
@@ -134,8 +134,8 @@ The basic types found in the `<cstdint>` header file are preferable.
| intptr_t | uintptr_t | The signed or unsigned integer type large enough to hold a pointer. |
## <a name="c2-5"></a> Variable Names
General variables are named in lowerCamelCase, including global variables, function parameters, local variables, and member variables.
## <a name="c2-5"></a>Variable Naming
General variables, including global variables, function parameters, local variables, and member variables, are named in the lowerCamelCase style.
```cpp
std::stringtableName;// Good: Recommended style.
std::stringtablename;// Bad: Forbidden style.
...
...
@@ -143,10 +143,10 @@ std::string path; // Good: When there is only one word, lowerCamelCase (al
```
### <a name="r2-5-1"></a>Rule 2.5.1 Add the prefix 'g_' to global variables. Do not add a prefix to a static variable.
Global variables should be used as little as possible, and special attention should be paid to the use of global variables. This prefix highlights global variables so that developers can be more careful when handling them.
Global variables should be used as little as possible, and special attention should be paid to their use. This prefix highlights global variables so that developers can be more careful when handling them.
- Global static variables and global variables are named in the same way.
- Static variables and common local variables in functions are named in the same way.
- Static member variables and common member variables of classes are named in the same way.
- Static variables in functions and common local variables are named in the same way.
- Static member variables in classes and common member variables are named in the same way.
```cpp
intg_activeConnectCount;
...
...
@@ -158,24 +158,24 @@ void Func()
}
```
### <a name="r2-5-2"></a>Rule 2.5.2 Name member variables in classes based on the three styles of the lowerCamelCase and maintain a uniform coding style for a product or project.
### <a name="r2-5-2"></a>Rule 2.5.2 Name member variables in classes in the unix\_like style.
```cpp
classFoo{
private:
std::stringfileName_;// Add the _ postfix, similar to the K&R naming style.
std::stringfileName_;// Add the underscore (\_) as the suffix, similar to the K&R naming style.
};
```
Use the lowerCamelCase style and do not add prefixes or suffixes to name a member variable of the struct or union type. Keep the naming style consistent with that for a local variable.
## <a name="c2-6"></a> Macro, Constant, and Enumeration Names
For macros and enumerated values, use all caps separated with underscores (`_`).
In the global scope, constants of named and unnamed namespaces and static member constants should be capitalized and separated with underscores (`_`). Local constants and ordinary const member variables use the lowerCamelCase naming style.
## <a name="c2-6"></a>Macro, Constant, and Enum Naming
Use uppercase letters separated by underscores (\_) for macro names and enumerated values.
In the global scope, constants of named and unnamed namespaces and static member constants should be capitalized and separated with underscores (\_).Local constants and ordinary const member variables use the lowerCamelCase naming style.
```cpp
#define MAX(a, b) (((a) < (b))? (b): (a)) // Though examples of Macro names are made, you are not advised to use macros to implement this function.
#define MAX(a, b) (((a) < (b))? (b): (a)) // Example of naming a macro only.
enumBaseColor{// Note: Enumerated types are named in the UpperCamelCase style, while their values are all capitalized and separated with underscores (_).
enumBaseColor{// Note: The enum type name is in the UpperCamelCase style, whereas the enumerated value is in uppercase letters separated by underscores (\_).
RED,
DARK_RED,
GREEN,
...
...
@@ -199,12 +199,12 @@ namespace Utils {
## <a name="c3-1"></a>Line Length
### <a name="a3-1-1"></a>Recommendation 3.1.1 Each line of code should contain a maximum of 120 characters.
** Note: **It is recommended that the number of characters in each line not exceed 120. It is recommended that the number of characters in each line not exceed 120. If the line of code exceeds the permitted length, wrap the line appropriately.
### <a name="r3-1-1"></a>Rule 3.1.1 Include 120 characters or less in each line.
If the line of code exceeds the permitted length, wrap the line appropriately.
Exception:
- If a one-line comment contains a command or URL of more than 120 characters, you can keep the line for ease in using copy, paste, and search using the grep command.
- The length of an `#include` statement can contain a long path exceeding 120 characters, but this should be avoided if possible.
Exceptions:
- If a line of comment contains a command or URL of more than 120 characters, you can keep the line for easy copy, paste, and search using the grep command.
- The #include and #error statements are allowed to exceed the line length requirement. However, you should try to avoid this.
- The error information in preprocessor directives can exceed the permitted length.
Put the error information of preprocessor directives in one line to facilitate reading and understanding even if the line contains more than 120 characters.
```cpp
...
...
@@ -217,22 +217,22 @@ Exception:
### <a name="r3-2-1"></a>Rule 3.2.1 Use spaces to indent and indent 4 spaces at a time.
Only spaces can be used for indentation. Four spaces are indented each time. Do not use the Tab character to indent.
Currently, almost all IDEs support automatic expansion of a Tab to 4 spaces upon pressing the tab key. Please configure your IDE to do so.
Currently, almost all integrated development environments (IDEs) support automatic conversion of a Tab input to four spaces. Configure your IDE to support indentation with spaces.
## <a name="c3-3"></a>Braces
### <a name="r3-3-1"></a>Rule 3.3.1 Use the K&R indentation writing style.
### <a name="r3-3-1"></a>Rule 3.3.1 Use the K&R indentation style.
__K&R style__
When a line break is required, the left brace of a function (excluding the lambda statement) starts a new line. One space should be placed between the statement and the brace.
The right brace starts a new line and nothing else is placed on the line, unless it is followed by the remaining part of the same statement, for example, "while" in the do statement, "else" or "else if" in the if statement, a comma, and a semicolon.
While wrapping a line, the left brace of the function (excluding the lambda statement) starts a new line and takes a single line. Other left braces are placed at the end of the line along with the statement.
The right brace takes a single line, unless it is followed by the rest of the same statement, such as `while` in the `do` statement, `else` or `else if` in the `if` statement, a comma, or a semicolon.
For example:
Example:
```cpp
structMyType{// Follow the statement to the end, and indent one space.
structMyType{// The left brace is placed at the end of the line along with the statement, and one space is used for indentation.
...
};
intFoo(inta)
{// The left brace of the function starts a new line, nothing else is placed on the line.
{// The left brace of the function starts a new line, and nothing else is placed on the line.
if(...){
...
}else{
...
...
@@ -259,13 +259,13 @@ private:
};
```
## <a name="c3-4"></a>Function Declarations and Definitions
## <a name="c3-4"></a>Function Declarations and Definitions
### <a name="r3-4-1"></a>Rule 3.4.1 The return type and the function name of a function declaration or definition must be on the same line. When the length of the function parameter list exceeds the permitted length, a line break is required and parameters must be aligned appropriately.
When a function is declared or defined, the return value type of the function should be on the same line as the function name. If the line length permits, the function parameters should be placed on the same line. Otherwise, the function parameters should be wrapped and properly aligned.
The left parenthesis of a parameter list should always be on the same line as the function name. The right parenthesis always follows the last parameter.
### <a name="r3-4-1"></a>Rule 3.4.1 Keep the return type and function name of the function declaration or definition in the same line, and align the function parameter list appropriately if it needs to be wrapped.
When a function is declared and defined, the return value type of the function should be in the same line as the function name. When the function parameter list is wrapped, it should be aligned appropriately.
The left parenthesis of a parameter list is always in the same line as the function name. The right parenthesis always follows the last parameter.
The following is an example of line breaks:
Example:
```cpp
ReturnTypeFunctionName(ArgTypeparamName1,ArgTypeparamName2)// Good: All are in the same line.
{
...
...
@@ -409,10 +409,10 @@ default: // Bad: default is not indented.
## <a name="c3-9"></a> Expressions
### <a name="a3-9-1"></a>Recommendation 3.9.1 Keep a consistent line break style for expressions and ensure that operators are placed at the end of a line.
### <a name="a3-9-1"></a>Rec 3.9.1 Keep a consistent line break style for expressions and ensure that operators are placed at the end of a line.
A long expression that does not meet the line length requirement must be wrapped appropriately. Generally, the expression is wrapped at an operator of a lower priority or a connector, and the operator or connector is placed at the end of the line.
Placing these at the end of a line indicates that the operation is to be continued on the next line.
For example:
Example:
// Assume that the first line exceeds the length limit.
```cpp
...
...
@@ -472,7 +472,7 @@ const int rank[] = {
```
## <a name="c3-12"></a> Pointers and References
### <a name="a3-12-1"></a>Recommendation 3.12.1 The pointer type `*` follows a variable name or type. There can be only one space to the side of it.
### <a name="a3-12-1"></a>Rec 3.12.1 The pointer type `*` follows a variable name or type. There can be only one space to the side of it.
Pointer naming: There can be only one space next to `*`.
```cpp
int*p=NULL;// Good
...
...
@@ -487,7 +487,7 @@ Exception: When a variable is modified by const or restrict, `*` cannot follow t
constchar*constVERSION="V100";
```
### <a name="a3-12-2"></a>Recommendation 3.12.2 The reference type `&` follows a variable name or type. There can be only one space to the side of it.
### <a name="a3-12-2"></a>Rec 3.12.2 The reference type `&` follows a variable name or type. There can be only one space to the side of it.
Reference naming: There can be only one space around `&`.
```cpp
inti=8;
...
...
@@ -502,39 +502,45 @@ int & p = i; // Bad
int&p=i;// Bad
```
## <a name="c3-13"></a> Preprocessor Directives
### <a name="r3-13-1"></a>Rule 3.13.1 The number sign (#) that starts a preprocessor directive must be at the beginning of the line and can be indented in nested preprocessor directives.
The number sign (#) that starts a preprocessor directive must be at the beginning of the line even through the preprocessor directive is inside a function.
```cpp
#if defined(__x86_64__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) // Good: "#" is at the beginning of the line.
#define ATOMIC_X86_HAS_CMPXCHG16B 1 // Good: "#" is at the beginning of the line.
#else
#define ATOMIC_X86_HAS_CMPXCHG16B 0
#endif
intFunctionName()
{
if(someThingError){
...
#ifdef HAS_SYSLOG // Good: Even in the function body, "#" is at the beginning of the line.
WriteToSysLog();
#else
WriteToFileLog();
#endif
}
}
```
The nested preprocessor directives starting with `#` can be indented and aligned based on a standardized style.
#define ATOMIC_X86_HAS_CMPXCHG16B 1 // Good: wrapped for easier comprehension
#else
#define ATOMIC_X86_HAS_CMPXCHG16B 0
#endif
```
## <a name="c3-13"></a>Compilation Preprocessing
### <a name="r3-13-1"></a>Rule 3.13.1 Place a number sign (#) at the beginning of a line for compilation preprocessing. In nested compilation preprocessing, the number sign (#) can be indented.
The number sign (#) must be placed at the beginning of a line for compilation preprocessing, even if the code is embedded in the function body.
### <a name="r3-13-2"></a>Rule 3.13.2 Do not use macros.
Macros do not obey scope, type system, and other rules, and may easily lead to issues. Avoid macro definitions wherever possible. If you must use macros, give them unique names.
In C++, there are many ways to avoid using macros:
- Use `const` or `enum` to define constants that are easy to understand.
- Use namespaces to avoid name conflicts.
- Use the `inline` function to avoid function call overhead.
- Use the `template` function to handle multiple types.
Macros can be used in scenarios such as header guards, conditional compilation, and logging.
### <a name="r3-13-3"></a>Rule 3.13.3 Do not use macros to represent constants.
Macros involve simple text replacement, which is completed during preprocessing. When an error occurs, the macro value is reported without the macro name. During tracing and debugging, the macro name is not displayed either. Besides, macros do not have type checking or scopes.
### <a name="r3-13-4"></a>Rule 3.13.4 Do not use function-like macros.
Before defining a function-like macro, consider whether it can be replaced with a function. If yes, you are advised to use a function for replacement.
The disadvantages of the function-like macro are as follows:
- Function-like macros have no type check, which is not as strict as the function call check.
- If macro parameters are not evaluated during macro expansion, unexpected results may occur.
- A macro has no independent scope.
- There are high skill requirements on the proper use of macros (for example, the usage of `#` and wide use of parentheses), which reduces readability.
- Extensions of some macros can only be implemented by specific compilers in specific scenarios, such as `statement expression` of `gcc`, reducing the portability.
- After the macro is expanded during precompilation, it is invisible during subsequent compilation, linking, and debugging. Besides, macros that contain multiple lines are expanded into a line. Function-like macros are difficult to debug, set breakpoints, and locate in case of bugs.
- Macros containing a large number of statements must be expanded at each call point. If there are many call points, the code will be expanded.
Unlike macros, functions do not have these disadvantages. However, the biggest disadvantage of functions is low execution efficiency (increasing the overhead of function calls and the difficulty of compiler optimization).
In light of this, you can use inline functions when necessary. Similar to macros, inline functions are expanded at the call point. The difference is that inline functions are expanded during compilation.
Inline functions have the advantages of both functions and macros:
- Strict type checking is performed for inline functions.
- Parameters are evaluated only once for inline functions.
- Inline functions are expanded in place and there is no overhead for function calls.
- Inline functions are better optimized than standard functions.
For performance-sensitive code, consider using inline functions instead of standard functions.
Exceptions:
In logging scenarios, only function-like macros can be used to keep information such as the file name (__FILE__) and line number (__LINE__) of the call point.
## <a name="c3-14"></a> Whitespace
### <a name="r3-14-1"></a>Rule 3.14.1 Ensure that horizontal spaces are used to highlight keywords and important information, and avoid unnecessary whitespace.
...
...
@@ -682,7 +688,7 @@ switch (value)
Note: Currently, all IDEs support automatic deletion of spaces at the end of a line. Please configure your IDE correctly.
### <a name="a3-14-1"></a>Recommendation 3.14.1 Use blank lines only when necessary to keep code compact.
### <a name="a3-14-1"></a>Rec 3.14.1 Use blank lines only when necessary to keep code compact.
There must be as few blank lines as possible so that more code can be displayed for easy reading. Recommendations:
- Add blank lines according to the correlation between lines.
...
...
@@ -809,7 +815,11 @@ Note: Example code in this document uses comments in the `//` format only to bet
*/
## <a name="c4-3"></a> Function Header Comments
### <a name="r4-3-1"></a> Rule 4.3.1 Function header comments with no content are forbidden.
### <a name="r4-3-1"></a>Rule 4.3.1 Write function header comments for public functions.
Public functions are interfaces provided by classes for external systems. To use public functions, the caller must understand the functionalities, parameter value ranges, return values, and precautions of the functions.
Write function header comments for the function value range, return value, and precautions, since they cannot be self-explained.
### <a name="r4-3-2"></a>Rule 4.3.2 Function header comments with no content are forbidden.
Not all functions need function header comments.
For information that cannot be described by function signatures, add function header comments.
...
...
@@ -911,7 +921,7 @@ int bar = 200; /* Comment on the right */
```
It is more appealing sometimes when the comment is placed on the right of code and the comments and code are aligned vertically.
After the alignment, ensure that the comment is 1–4 spaces away from the widest line of code.
For example:
Example:
```cpp
constintA_CONST=100;/* Related comments of the same type can be aligned vertically. */
...
...
@@ -925,10 +935,10 @@ The correct method is to delete unnecessary code directly. If necessary, conside
Here, commenting out refers to the removal of code from compilation without actually deleting it. This is done by using /* */, //, #if 0, #ifdef NEVER_DEFINED, and so on.
TODO/TBD comments are used to describe required improvements and supplements.
FIXME comments are used to describe defects that need fixing.
They should have a standardized style, which facilitates text search. For example:
They should have a standardized style, which facilitates text search. Example:
```cpp
// TODO(<author-name>): XX
...
...
@@ -942,12 +952,12 @@ A header file is an external interface of a module or file. The design of a head
The interface declaration for most functions is more suitable placed in the header file, but implementation (except inline functions) cannot be placed in the header file. Functions, macros, enumerations, and structure definitions that need to be used in .cpp files cannot be placed in the header file.
The header responsibility should be simple. An overly complex header file will make dependencies complex and cause long compilation times.
### <a name="a5-1-1"></a>Recommendation 5.1.1 Each .cpp file should have a .h file with the same name. It should be used to declare the classes and interfaces that need to be exposed externally.
### <a name="a5-1-1"></a>Rec 5.1.1 Each .cpp file should have a .h file with the same name. It should be used to declare the classes and interfaces that need to be exposed externally.
Generally, each .cpp file has a corresponding .h file. This .cpp file is used to store the function declarations, macro definitions, and class definitions that are to be exposed.
If a .cpp file does not need to open any interface externally, it should not exist.
Exception: __An entry point (for example, the file where the main function is located), unit tests, and dynamic libraries __
For example:
Example:
```cpp
// Foo.h
...
...
@@ -1059,7 +1069,7 @@ int Fun()
}
```
In some scenarios, if internal functions need to be referenced with no intrusion to the code, the extern declaration mode can be used.
For example:
Example:
When performing unit testing on an internal function, you can use the extern declaration to reference the tested function.
When a function needs to be stubbed or patched, the function can be declared using extern.
...
...
@@ -1106,10 +1116,10 @@ extern "C" {
According to the author of a.h, the function Foo is a C++ free function following the "C++" link specification.
However, because `#include "a.h"` is placed inside `extern "C"` in b.h, the link specification of function Foo is changed incorrectly.
Exception:
Exceptions:
In the C++ compilation environment, if you want to reference the header file of pure C, the C header files should not contain `extern "C"`. The non-intrusive approach is to include the C header file in `extern "C"`.
### <a name="a5-2-1"></a>Recommendation 5.2.1 Use `#include` instead of a forward declaration to include header files.
### <a name="a5-2-1"></a>Rec 5.2.1 Use `#include` instead of a forward declaration to include header files.
A forward declaration is for the declaration of classes, functions, and templates and is not meant for its definition.
- Advantages:
...
...
@@ -1129,7 +1139,7 @@ Therefore, we should avoid using forward declarations as much as possible. Inste
## <a name="c6-1"></a> Namespaces
### <a name="a6-1-1"></a>Recommendation 6.1.1 For code that does not need to be exported from the .cpp file, you are advised to use an unnamed namespace for encapsulation or use static to modify the variables, constants, or functions that need hiding.
### <a name="a6-1-1"></a>Rec 6.1.1 For code that does not need to be exported from the .cpp file, you are advised to use an unnamed namespace for encapsulation or use static to modify the variables, constants, or functions that need hiding.
In the C++ 2003 standard, using static to modify the external availability of functions and variables was marked as deprecated. Therefore, unnamed namespaces are the recommended method.
Main Reasons:
...
...
@@ -1210,7 +1220,7 @@ namespace Foo {
## <a name="c6-2"></a> Global Functions and Static Member Functions
### <a name="a6-2-1"></a>Recommendation 6.2.1 Use namespaces to manage global functions. If global functions are closely tied to a class, you can use static member functions.
### <a name="a6-2-1"></a>Rec 6.2.1 Use namespaces to manage global functions. If global functions are closely tied to a class, you can use static member functions.
Note: Placing non-member functions in a namespace avoids polluting the global scope. Do not use "class + static member function" to simply manage global functions. If a global function is closely tied to a class, it can be used as a static member function of the class.
If you need to define some global functions for a .cpp file, use unnamed namespaces for management.
...
...
@@ -1227,7 +1237,7 @@ public:
## <a name="c6-3"></a> Global Constants and Static Member Constants
### <a name="a6-3-1"></a>Recommendation 6.3.1 Use namespaces to manage global constants. If global constants are closely tied to a class, you can use static member constants.
### <a name="a6-3-1"></a>Rec 6.3.1 Use namespaces to manage global constants. If global constants are closely tied to a class, you can use static member constants.
Note: Placing global constants in a namespace avoids polluting the global scope. Do not use "class + static member constant" to simply manage global constants. If a global constant is closely tied to a class, it can be used as a static member constant of the class.
If you need to define some global constants only for a .cpp file, use unnamed namespaces for management.
...
...
@@ -1244,7 +1254,7 @@ public:
## <a name="c6-4"></a> Global Variables
### <a name="a6-4-1"></a>Recommendation 6.4.1 Do not use global variables. Use the singleton pattern instead.
### <a name="a6-4-1"></a>Rec 6.4.1 Do not use global variables. Use the singleton pattern instead.
Note: Global variables can be modified and read, which results in data coupling between production code and the global variables.
### <a name="r7-1-1"></a>Rule 7.1.1 The member variables of a class must be initialized explicitly.
Note: If a class has members but no constructor and a default constructor is defined, the compiler will automatically generate a constructor, but it will not initialize member variables. The content of each object is uncertain.
Exception:
Exceptions:
- If the member variables in a class have a default constructor, explicit initialization is not required.
Example: The following code has no constructor, and private data members cannot be initialized:
...
...
@@ -1323,7 +1333,7 @@ class Message {
public:
voidProcessOutMsg()
{
//…
//...
}
private:
...
...
@@ -1345,7 +1355,7 @@ public:
voidProcessOutMsg()
{
// …
// ...
}
private:
...
...
@@ -1356,7 +1366,7 @@ private:
};
```
### <a name="a7-1-1"></a>Recommendation 7.1.1 Initialization during declaration (C++ 11) and initialization using the constructor initialization list are preferred for member variables.
### <a name="a7-1-1"></a>Rec 7.1.1 Initialization during declaration (C++ 11) and initialization using the constructor initialization list are preferred for member variables.
Note: Initialization during declaration (C++11) is preferred because initialized values of member variables can be easily understood. If initialized values of certain member variables are relevant to constructors, or C++ 11 is not supported, the constructor initialization list is used preferentially to initialize these member variables. Compared with the assignment statements in constructors, code of the constructor initialization list is simpler and has higher performance, and can be used to initialize constant and reference members.
```cpp
...
...
@@ -1417,6 +1427,29 @@ private:
```
2. Use delete provided by C++ 11. For details, see Rule 10.1.3 in chapter 10 Modern C++ Features.
3. You are advised to inherit **NoCopyable** and **NoMovable**. Do not use macros such as **DISALLOW_COPY_AND_MOVE**, **DISALLOW_COPY**, and **DISALLOW_MOVE**.
```cpp
classFoo:publicNoCopyable,publicNoMovable{
};
```
Implementation of NoCopyable and NoMovable:
```cpp
classNoCopyable{
public:
NoCopyable()=default;
NoCopyable(constNoCopyable&)=delete;
NoCopyable&operator=(NoCopyable&)=delete;
};
classNoMovable{
public:
NoMovable()=default;
NoMovable(NoMovable&&)noexcept=delete;
NoMovable&operator=(NoMovable&&)noexcept=delete;
};
```
### <a name="r7-1-4"></a>Rule 7.1.4 Copy constructors and copy assignment operators should be implemented or forbidden together.
Both copy constructors and copy assignment operators provide copy semantics. They should be implemented or hidden together.
...
...
@@ -1502,10 +1535,40 @@ When running the following statement:
The constructor of the derived class is executed first. However, the constructor of the base class is called first. Because the constructor of the base class calls the virtual function log, the log is in the base class version. The derived class is constructed only after the base class is constructed. As a result, behaviors of non-polymorphism are caused.
This also applies to destructors.
### <a name="r7-1-7"></a>Rule 7.1.7 The copy constructors, copy assignment operators, move constructors, and move assignment operators in polymorphic base classes must be non-public or delete functions.
Slicing occurs if the value of a derived class object is directly assigned to a base class object. In this case, only the base class part is copied or moved, which undermines polymorphism.
[Counterexample]
In the following code example, the copy constructor and copy assignment operator are not defined in the base class. The compiler automatically generates two special member functions.
If the value of a derived class object is assigned to the base class object, slicing occurs. The copy constructor and copy assignment operator can be declared as **deleted** so that the compiler can check such assignment behavior.
other.Fun();// The Fun() function of the base class is called.
}
```
```cpp
Derivedd;
Foo(d);// A derived class object is passed.
```
1. Set copy constructors or copy assignment operators to **private** and do not implement them.
## <a name="c7-2"></a> Inheritance
### <a name="r7-2-1"></a>Rule 7.2.1 Destructors of a base class should be declared as virtual.
### <a name="r7-2-1"></a>Rule 7.2.1 Declare destructors of a base class as virtual, and declare the class that is not to be inherited as final.
Note: Destructors of the derived class can be called during polymorphism invocation only when destructors of the base class are virtual.
Example: There will be memory leak if destructors of the base class are not declared as virtual.
...
...
@@ -1564,7 +1627,8 @@ int main(int argc, char* args[])
}
```
Because destructors of the base class are not declared as virtual, only destructors of the base class are called when an object is destroyed. Destructors of the derived class Sub are not called. As a result, a memory leak occurs.
Exceptions:
For classes such as **NoCopyable** and **NoMovable** that have no behavior and are used only as identifiers, you do not need to define them as final.
### <a name="r7-2-2"></a>Rule 7.2.2 Do not use default parameter values for virtual functions.
Note: In C++, virtual functions are dynamically bound, but the default parameters of functions are statically bound during compilation. This means that the function you finally execute is a virtual function that is defined in the derived class but uses the default parameter value in the base class. To avoid confusion and other problems caused by inconsistent default parameter declarations during overriding of virtual functions, it is prohibited to declare default parameter values for all virtual functions.
...
...
@@ -1642,7 +1706,7 @@ Multiple inheritance provides a simpler method for assembling and reusing multip
Therefore, multiple inheritance can be used only in the following cases:
### <a name="a7-3-1"></a>Recommendation 7.3.1 Use multiple inheritance to implement interface separation and multi-role combination.
### <a name="a7-3-1"></a>Rec 7.3.1 Use multi-inheritance to implement interface separation and multi-role combination.
If a class requires multiple interfaces, combine multiple separated interfaces by using multiple inheritance. This is similar to the Traits mixin of the Scala language.
```cpp
...
...
@@ -1695,7 +1759,7 @@ It is recommended that you split a long function into several functions that are
## <a name="c8-2"></a>Inline Functions
### <a name="a8-2-1"></a>Recommendation 8.2.1 An inline function cannot exceed 10 lines.
### <a name="a8-2-1"></a>Rec 8.2.1 An inline function cannot exceed 10 lines.
**Note**: An inline function has the same characteristics of a normal function. The difference between an inline function and a normal function lies in the processing of function calls. When a general function is called, the program execution right is transferred to the called function, and then returned to the function that calls it. When an inline function is called, the invocation expression is replaced with an inline function body.
Inline functions are only suitable for small functions with only 1-10 lines. For a large function that contains many statements, the function call and return overheads are relatively trivial and do not need the help of an inline function. Most compilers may abandon the inline mode and use the common method to call the function.
...
...
@@ -1705,7 +1769,7 @@ If an inline function contains complex control structures, such as loop, branch
## <a name="c8-3"></a> Function Parameters
### <a name="a8-3-1"></a>Recommendation 8.3.1 Use a reference instead of a pointer for function parameters.
### <a name="a8-3-1"></a>Rec 8.3.1 Use a reference instead of a pointer for function parameters.
**Note**: A reference is more secure than a pointer because it is not empty and does not point to other targets. Using a reference stops the need to check for illegal null pointers.
...
...
@@ -1714,7 +1778,7 @@ Use const to avoid parameter modification, so that readers can clearly know that
Exception: When the input parameter is an array with an unknown compile-time length, you can use a pointer instead of a reference.
### <a name="a8-3-2"></a>Recommendation 8.3.2 Use strongly typed parameters. Do not use void*.
### <a name="a8-3-2"></a>Rec 8.3.2 Use strongly typed parameters. Do not use void*.
While different languages have their own views on strong typing and weak typing, it is generally believed that C and C++ are strongly typed languages. Since we use such a strongly typed language, we should keep to this style.
An advantage of this is the compiler can find type mismatch problems at the compilation stage.
...
...
@@ -1749,7 +1813,7 @@ void MakeTheList()
1. You can use a template function to change the parameter type.
2. A base class pointer can be used to implement this according to polymorphism.
### <a name="a8-3-3"></a>Recommendation 8.3.3 A function can have a maximum of five parameters.
### <a name="a8-3-3"></a>Rec 8.3.3 A function can have a maximum of five parameters.
If a function has too many parameters, it is apt to be affected by external changes, and therefore maintenance is affected. Too many function parameters will also increase the testing workload.
If a function has more than five parameters, you can:
...
...
@@ -1776,7 +1840,7 @@ const int MAX_MSISDN_LEN = 20; // Good
constexprintMAX_MSISDN_LEN=20;
```
### <a name="a9-1-1"></a>Recommendation 9.1.1 A group of related integer constants must be defined as an enumeration.
### <a name="a9-1-1"></a>Rec 9.1.1 A group of related integer constants must be defined as an enumeration.
**Note**: Enumerations are more secure than `#define` or `const int`. The compiler checks whether a parameter value is within the enumerated value range to avoid errors.
...
...
@@ -1900,7 +1964,7 @@ Even if a class of the aggregate type is directly copied and compared, and any f
For details about the POD type, see the appendix.
### <a name="a9-1-2"></a>Recommendation 9.1.2 Declare and initialize variables only when they are used.
### <a name="a9-1-2"></a>Rec 9.1.2 Declare and initialize variables only when they are used.
**Note**: It is a common low-level programming error that a variable is not assigned an initial value before being used. Declaring and initializing a variable just before using it will prevent this.
...
...
@@ -1951,7 +2015,7 @@ x = Func(i, i);
### <a name="r9-2-2"></a>Rule 9.2.2 A switch statement must have a default branch.
In most cases, a switch statement requires a default branch to ensure that there is a default action when the case tag is missing for a processed value.
Exception:
Exceptions:
If the switch condition variables are enumerated and the case branch covers all values, the default branch is redundant.
Because modern compilers can check which case branches are missing in the switch statement and provide an advanced warning.
...
...
@@ -1973,7 +2037,7 @@ switch (color) {
}
```
### <a name="a9-2-1"></a>Recommendation 9.2.1 When comparing expressions, follow the principle that the left side tends to change and the right side tends to remain unchanged.
### <a name="a9-2-1"></a>Rec 9.2.1 When comparing expressions, follow the principle that the left side tends to change and the right side tends to remain unchanged.
When a variable is compared with a constant, placing the constant on the left, for example, if (MAX == v), does not comply with standard reading habits and is more difficult to understand.
The constant should be placed on the right. The expression is written as follows:
```cpp
...
...
@@ -1989,7 +2053,7 @@ There are special cases: for example, if the expression `if (MIN < value && valu
You do not need to worry about writing '==' as '=' because a compilation alarm will be generated for `if (value = MAX)` and an error will be reported by other static check tools. Use these tools to solve such writing errors and ensure that that code is readable.
### <a name="a9-2-2"></a>Recommendation 9.2.2 Use parentheses to specify the operator precedence.
### <a name="a9-2-2"></a>Rec 9.2.2 Use parentheses to specify the operator precedence.
Use parentheses to specify the operator precedence. This will prevent program errors due to the inconsistency between default priority and the intended design. At the same time, it makes the code clearer and more readable. However, too many parentheses muddy the code, reducing readability. The following is a recommendation on their correct usage.
- For binary and ternary operators, if multiple operators are involved, parentheses should be used.
...
...
@@ -2033,15 +2097,15 @@ The type casting provided by C++ is more targeted, easy to read, and more secure
int64_ti{someInt32};
```
### <a name="a9-3-1"></a>Recommendation 9.3.1 Avoid using `dynamic_cast`.
### <a name="a9-3-1"></a>Rec 9.3.1 Avoid using `dynamic_cast`.
1.`dynamic_cast` depends on the RTTI of C++ so that the programmer can identify the type of the object in C++ at run time.
2.`dynamic_cast` indicates that a problem has occurred in the design of the base class and derived class.The derived class destroys the contract of the base class and it is necessary to use `dynamic_cast` to convert the class to a subclass for special processing. In this case, it is more desirable to improve the design of the class, instead of using `dynamic_cast` to solve the problem.
### <a name="a9-3-2"></a>Recommendation 9.3.2 Avoid using `reinterpret_cast`.
### <a name="a9-3-2"></a>Rec 9.3.2 Avoid using `reinterpret_cast`.
**Note**: `reinterpret_cast` is used to convert irrelevant types. Trying to use `reinterpret_cast` to force a type to another type destroys the security and reliability of the type and is an insecure casting method. Avoid casting between completely different types.
### <a name="a9-3-3"></a>Recommendation 9.3.3 Avoid using `const_cast`.
### <a name="a9-3-3"></a>Rec 9.3.3 Avoid using `const_cast`.
**Note**: The `const_cast` command is used to remove the `const` and `volatile` properties of an object.
...
...
@@ -2109,7 +2173,7 @@ delete[] numberArray;
numberArray=NULL;
```
### <a name="a9-4-1"></a>Recommendation 9.4.1 Use the RAII feature to trace dynamic allocation.
### <a name="a9-4-1"></a>Rec 9.4.1 Use the RAII feature to trace dynamic allocation.
Note: RAII is an acronym for Resource Acquisition Is Initialization. It is a simple technology that controls program resources (such as memory, file handle, network connections, and mutexes) by using the object lifecycle.
...
...
@@ -2125,12 +2189,12 @@ class LockGuard {
public:
LockGuard(constLockType&lockType):lock_(lockType)
{
lock_.Acquire();
lock_.Aquire();
}
~LockGuard()
{
lock_.Release();
lock_.Relase();
}
private:
...
...
@@ -2187,10 +2251,10 @@ void Fun2()
Exception: In rare cases where high performance coding is required , you can temporarily save the pointer returned by string::c_str() to match the existing functions which support only the input parameters of the const char* type. However, you should ensure that the lifecycle of the string object is longer than that of the saved pointer, and that the string object is not modified within the lifecycle of the saved pointer.
### <a name="a9-5-1"></a>Recommendation 9.5.1 Use std::string instead of char*.
### <a name="a9-5-1"></a>Rec 9.5.1 Use std::string instead of char*.
Note: Using string instead of `char*` has the following advantages:
1. There is no need to consider the null character ’\0’at the end.
1. There is no need to consider the null character '\0' at the end.
2. You can directly use operators such as `+`, `=`, and `==`, and other character and string operation functions.
3. There is no need to consider memory allocation operations.This helps avoid explicit usage of `new` and `delete` and the resulting errors.
...
...
@@ -2216,7 +2280,7 @@ Exception:
Before the C++ 11 standard is widely used, std::auto_ptr can be used in scenarios where ownership needs to be transferred. However, it is recommended that std::auto_ptr be encapsulated. The copy constructor and assignment operator of the encapsulation class should not be used in a standard container.
### <a name="a9-5-2"></a>Recommendation 9.5.2 Use the new standard header files.
### <a name="a9-5-2"></a>Rec 9.5.2 Use the new standard header files.
Note:
When using the standard header file of C++, use `<cstdlib>` instead of `<stdlib.h>`.
...
...
@@ -2257,7 +2321,7 @@ private:
};
```
### <a name="a9-6-1"></a>Recommendation 9.6.1 Member variables that will not be modified after initialization should be defined as constants.
### <a name="a9-6-1"></a>Rec 9.6.1 Member variables that will not be modified after initialization should be defined as constants.
```cpp
classFoo{
...
...
@@ -2270,7 +2334,7 @@ private:
## <a name="c9.7"></a> Exceptions
### <a name="a9-7-1"></a>Recommendation 9.7.1 If the function does not throw an exception, the declaration is `noexcept`.
### <a name="a9-7-1"></a>Rec 9.7.1 If the function does not throw an exception, the declaration is `noexcept`.
**Reasons:**
1. If the function does not throw an exception, the declaration is `noexcept`, which enables the compiler to optimize the function to the maximum extent, for example, reducing the execution paths and improving the efficiency of exiting when an error occurs.
2. For STL containers such as `vector`, to ensure the interface robustness, if the `move ` constructor of saved items is not declared as `noexcept`, the `copy machanism` instead of the `move machanism` is used when the items are removed from the container. This would cause performance loss risks. If the function does not throw an exception, or a program does not intercept and process an exception thrown by the function, new `noexcept` keywords can be used to modify the function, indicating that the function does not throw an exception or the thrown exception is not intercepted or processed. For example:
...
...
@@ -2317,21 +2381,26 @@ a2.push_back(Foo2()); //Triggers container expansion and invokes the move constr
**Note**
The default constructor, destructor, `swap` function, and `move` operator should not throw an exception.
## <a name="c9-8"></a> Templates
## <a name="c9-8"></a>Templates and Generic Programming
Template programming allows for extremely flexible interfaces that are type safe and high performance, enabling reuse of code of different types but with the same behavior.
### <a name="a9-8-1"></a>Rule 9.8.1 Do not use generic programming.
OpenHarmony adopts object-oriented programming, which has ideas, concepts, and techniques totally different from those of generic programming.
The disadvantages of template proramming are as follows:
Generic programming in C++ allows for extremely flexible interfaces that are type safe and high performance, enabling reuse of code of different types but with the same behavior.
1. The techniques used in template programming are often obscure to anyone but language experts. Code that uses templates in complicated ways is often unreadable, and is hard to debug or maintain.
2. Template programming often leads to extremely poor compiler time error messages: even if an interface is simple, complicated implementation details become visible when the user does something wrong.
3. If the template is not properly used, the code will be over expanded during runtime.
4. It is difficult to modify or refactor template code. The template code is expanded in multiple contexts, and it is hard to verify that the transformation makes sense in all of them.
However, generic programming has the following disadvantages:
Therefore, it is recommended that __ template programming be used only in a small number of basic components and basic data structure__. When using the template programming, minimize the __ complexity as much as possible, and __ avoid exposing the template__. It is better to hide programming as an implementation detail whenever possible, so that user-facing headers are readable. And you should write sufficiently detailed comments for code that uses templates.
1. People who are not familiar with generic programming often write into templates object-oriented logic or members that do not depend on template parameters, making the code unreadable or bloated.
2. The techniques used in generic programming are often obscure to anyone but language experts. Template code can be unreadable in certain cases.
3. Generic programming often leads to extremely poor compile time error messages. The uncalled-for implementation details of APIs are displayed to users in the error messages, making even a simple API difficult to debug.
4. Inappropriate use of templates cause code bloat during runtime.
5. It is difficult to modify or refactor the template code. The template code is expanded in multiple contexts, and it is hard to verify that the code refactoring makes sense in all of them.
Only __a few components of OpenHarmony__ support generic programming, and the templates developed using generic programming must have detailed comments.
Exceptions:
1. The STL adaptation layer can use generic programming.
## <a name="c9-9"></a>Macros
## <a name="c9-9"></a>Macros
In the C++ language, it is strongly recommended that complex macros be used as little as possible.
- For constant definitions, use `const` or `enum` as stated in the preceding sections.
- For macro functions, try to be as simple as possible, comply with the following principles, and use inline functions and template functions for replacement.
...
...
@@ -2353,7 +2422,7 @@ As the ISO released the C++ 11 language standard in 2011 and released the C++ 17
This chapter describes some guidelines for modern C++ use, to avoid language pitfalls.
## <a name="c10-1"></a> Code Simplicity and Security Improvement
### <a name="a10-1-1"></a>Recommendation 10.1.1 Use `auto` properly.
### <a name="a10-1-1"></a>Rec 10.1.1 Use `auto` properly.
**Reasons**
*`auto` can help you avoid writing verbose, repeated type names, and can also ensure initialization when variables are defined.
...
...
@@ -2593,26 +2662,53 @@ void func()
```
## <a name="c10-2"></a> Smart Pointers
### <a name="r10-2-1"></a>Rule 10.2.1 Use smart pointers instead of a raw pointer to manage resources.
### <a name="r10-2-1"></a>Rule 10.2.1 Preferentially use the original pointer source instead of the smart pointer for singletons and class members that are not held by multiple parties.
**Reason:**
Avoid resource leakage.
Smart pointers automatically release object resources to prevent resource leakage, but they require extra resource overheads. For example, the classes, constructors, and destructors automatically generated by smart pointers consume more resources such as memory.
When singletons, class members, and the like are not held by multiple parties, resources can be released only in class destructors. In this case, smart pointers should not be used.
**Example:**
```cpp
voidUse(inti)
{
autop=newint{7};// Bad: Initializing local pointers with new.
autoq=std::make_unique(9);// Good: Guarantee that memory is released.
if(i>0){
return;// Return and possible leak.
classFoo;
classBase{
public:
Base(){}
virtual~Base()
{
deletefoo_;
}
deletep;// Too late to salvage.
}
private:
Foo*foo_=nullptr;
};
```
**Exception:**
Raw pointers can be used in scenarios requiring high performance and compatibility.
**Exceptions**
1. When a created object is returned, a smart pointer can be used if the pointer destructor is required.
```cpp
classUser;
classFoo{
public:
std::unique_ptr<User,void(User*)>CreateUniqueUser()// Use unique_ptr to ensure that the object is created and released in the same runtime.
`std::make_unique` does not support user-defined `deleter`.
In the scenario where the `deleter` needs to be customized, it is recommended that `make_unique` be implemented in the customized version’s own namespace.
In the scenario where the `deleter` needs to be customized, it is recommended that `make_unique` be implemented in the customized version's own namespace.
Using `new` to create `unique_ptr` with the user-defined `deleter` is the last choice.
### <a name="r10-2-4"></a>Rule 10.2.4 Create `shared_ptr` by using `std::make_shared` instead of `new`.
...
...
@@ -2670,7 +2766,7 @@ When `std::make_shared` creates `std::shared_ptr`, it allocates sufficient memor
Similar to `std::make_unique`, `std::make_shared` does not support `deleter` customization.
## <a name="c10-3"></a> Lambda
### <a name="a10-3-1"></a>Recommendation 10.3.1 Use `lambda` to capture local variables or write local functions when normal functions do not work.
### <a name="a10-3-1"></a>Rec 10.3.1 Use `lambda` to capture local variables or write local functions when normal functions do not work.
**Reason:**
Functions cannot capture local variables or be declared at local scope. If you need those things, choose `lambda` instead of handwritten `functor`.
On the other hand, `lambda` and `functor` objects do not support overloading. If overloading is required, use a function.
...
...
@@ -2720,7 +2816,7 @@ void Foo()
}
```
### <a name="a10-3-2"></a>Recommendation 10.3.2 All variables are explicitly captured if `this` is captured.
### <a name="a10-3-2"></a>Rec 10.3.2 All variables are explicitly captured if `this` is captured.
**Reason:**
The `[=]` in the member function seems to indicate capturing by value but actually it is capturing data members by reference because it captures the invisible `this` pointer by value. Generally, it is recommended that capturing by reference be avoided. If it is necessary to do so, write `this` explicitly.
The lambda expression provides two default capture modes: by-reference (&) and by-value (=).
By default, the "by-reference" capture mode will implicitly capture the reference of all local variables, which will easily lead to dangling references. By contrast, explicitly writing variables that need to be captured can make it easier to check the lifecycle of an object and reduce the possibility of making a mistake.
By default, the "by-value” capture mode will implicitly capture this pointer, and it is difficult to find out which variables the lambda function depends on. If a static variable exists, the reader mistakenly considers that the lambda has copied a static variable.
By default, the "by-value" capture mode will implicitly capture this pointer, and it is difficult to find out which variables the lambda function depends on.If a static variable exists, the reader mistakenly considers that the lambda has copied a static variable.
Therefore, it is required to clearly state the variables that lambda needs to capture, instead of using the default capture mode.
**Bad example:**
...
...
@@ -2786,7 +2882,7 @@ auto func()
Reference: Effective Modern C++: Item 31: Avoid default capture modes.
## <a name="c10-4"></a> Interfaces
### <a name="a10-4-1"></a>Recommendation 10.4.1 Use `T*` or `T&` arguments instead of a smart pointer in scenarios where ownership is not involved.
### <a name="a10-4-1"></a>Rec 10.4.1 Use `T*` or `T&` arguments instead of a smart pointer in scenarios where ownership is not involved.
**Reasons:**
1. Passing a smart pointer to transfer or share ownership should only be used when the ownership mechanism is explicitly required.
2. Passing a smart pointer (for example, passing the `this` smart pointer) restricts the use of a function to callers using smart pointers.