Summary
This is the second article about how to avoid some errors at the beginning of encoding. In our first article, we proposed that we should avoid integrating a large number of computations into an expression as much as possible. However, we will discuss this issue in depth here. Next let's take a look at the dangerous factors in complex expressions and how we can avoid a large number of logical errors.
Introduction
You can click here to read our first article. This time, we will list some error examples from different famous projects to emphasize their universality. All the errors I showed here were found with the help of the PVS-Studio analyzer, with a relatively wide time range. I have basically told all of these errors to the developers of the relevant project, and I hope they can fix these defects in the new code revision. The reason why I mentioned this in the introduction section is that every time I post similar articles, I will receive many letters asking me to "inform the project developers of the errors ".
1. Do not use the ternary operator "? :"
The Trielement conditional operation uses the : "Operator C/C ++ code. This operation returns the second or third operand, depending on the value of the logical expression defined by the first operand. For example:
Int minValue = A <B? A: B;
The priority of ternary operations is very low (see the table below), and programmers often forget this, which is why I say that ternary operations are very dangerous.
Figure 1-C/C ++ operators sorted by priority from high to low
Note that "? : "The operation has a lower priority than addition, multiplication, bit, or operator. Analyze the following code:
Int Z = X + (A = B )? 1: 2;
The actual calculation method of this Code may be very different from what is displayed on the surface. The most likely case is that the programmer wants to add 1 or 2 to the X value based on the (A = B) condition, and in the code above, "X + (A = B) "expression is the condition. The result is that the code written in the project is:
Int Z = (X + (A = B ))? 1: 2;
However, the programmer's intention is:
Int Z = X + (A = B? 1: 2 );
I think the first thought of this is that it is important to master the computing priority. However, although programmers usually know the computing priority, this ternary operation is too tricky! Not only new users, but experienced programmers also make such mistakes. Even in the Code with the best quality, you can easily find errors related to ternary computation. The following lists several examples.
V502 Perhaps '? : 'Operator works in a different way than it was expected. '? : 'Operator has a lower priority than the '*' operator. physics dgminkowskiconv. cpp 1061
DgInt32 CalculateConvexShapeIntersection (...)
{
...
Den = dgFloat32 (1.0e-24f )*
(Den> dgFloat32 (0.0f ))?
DgFloat32 (1.0f): dgFloat32 (-1.0f );
...
}
V502 Perhaps '? : 'Operator works in a different way than it was expected. '? : 'Operator has a lower priority than the '-'operator. views custom_frame_view.cc 400
Static Const IntKClientEdgeThickness;
IntHeight ()Const;
BoolShouldShowClientEdge ()Const;
VoidCustomFrameView: PaintMaximizedFrameBorder (gfx: Canvas * canvas ){
...
IntEdge_height = titlebar_bottom-> height ()-
ShouldShowClientEdge ()? KClientEdgeThickness: 0;
...
}
V502 Perhaps '? : 'Operator works in a different way than it was expected. '? : 'Operator has a lower priority than the '| 'operator. vm vm_file_win.c 393
# Define FILE_ATTRIBUTE_NORMAL 0x00000080
# Define FILE_FLAG_NO_BUFFERING 0x20000000
Vm_file * vm_file_fopen (...)
{
...
Mds [3] = FILE_ATTRIBUTE_NORMAL |
(Islog = 0 )? 0: FILE_FLAG_NO_BUFFERING;
...
}
From the above example, we can see that this type of errors is very noteworthy, which is why I will discuss them separately. The scope of such errors is quite extensive. I can list more examples, but all of them are similar.
This error can be avoided if you can discard attempts to merge multiple operations in a single line of code. Or, if you still insist on doing so, do not use parentheses. I will talk about parentheses later. Next, let's analyze and use "? : "How to avoid potential errors.
No doubt, "? : "Operator is a syntactic sugar. In most cases, you can use" if "to replace it. There are only a few exceptions, such as when you execute a reference initialization task:
MyObject & ref = X? A: B;
MyObject * tmpPtr;
If (X)
TmpPtr = &;
Else
TmpPtr = & B;
MyObject & ref = * tmpPtr;
Therefore, we should not refuse to use "? : "Operator, but it is likely to cause errors when used. In consideration of this situation, I have developed a guideline for myself: Be sure to : "The computation result of the operator is immediately stored at the specified location and cannot be mixed with any other behavior. In other words, "? : "The left side of the operator condition must have an assignment operation. The following example is returned:
Int Z = X + (A = B )? 1: 2;
We recommend that you use the following code:
Int Z = X;
Z + = A = B? 1: 2;
If it is an IPP sample code example, I will write it like this:
Mds [3] = FILE_ATTRIBUTE_NORMAL;
Mds [3] | = (islog = 0 )? 0: FILE_FLAG_NO_BUFFERING;
You may not agree with my suggestion. I will not explain it more. For example, for myself, if one line of code can be used to solve the problem, I do not like to use two or more lines of code. Another effective alternative is to use parentheses to convert "? : "Operator. My main task is to point out the error mode. The specific error protection mode depends on the programmer's personal preferences.
2. bold use of parentheses
For some reason, it is a shame to use extra parentheses in C/C ++ programming. Perhaps this is because the operator priority is frequently asked during interviews, and people start to subconsciously try to use the priority mechanism in various situations. If someone adds additional parentheses, others will think that he is a newbie, not a real programmer.
I have even seen some discussions on the Internet. Some of them are very doggy and think that using extra parentheses is a bad habit. If you are not sure about the expression calculation method, you should study it again, do not write programs. Unfortunately, I did not find what I saw at the time, but I did not agree with this idea. Of course, you must understand the operational priority, but if you use different types of operations in an expression, it is best to use parentheses to protect yourself from making mistakes. This not only helps you avoid errors, but also ensures that other developers can understand the code.
Not only new programmers, but skilled old programmers also make mistakes due to mixed priorities. Expressions are not necessarily very complex and long. Even relatively simple expressions may encounter errors. See the following example:
V564 The '&' operator is applied to bool type value. You 've probably forgotten to include parentheses or intended to use the' & 'operator. game g_client.c 1534
# Define SVF_CASTAI 0x00000010
Char* ClientConnect (...){
...
If(! Ent-> r. svFlags & SVF_CASTAI ){
...
}
V564 The '&' operator is applied to bool type value. You 've probably forgotten to include parentheses or intended to use the' & 'operator. dosbox sdlmain. cpp 519
StaticSDL_Surface * GFX_SetupSurfaceScaled (Bit32u sdl_flags,
Bit32u bpp ){
...
If(! Sdl. bsurface. surface | (! Sdl. Bags. surface-> flags & SDL_HWSURFACE )){
...
}
Another example from Chromium is as follows:
V564 The '&' operator is applied to bool type value. You 've probably forgotten to include parentheses or intended to use the' & 'operator. base platform_file_win.cc 216
# Define file_attribute_directories 0x00000010
BoolGetPlatformFileInfo (PlatformFile file, PlatformFileInfo * info ){
...
Info-> is_directory =
File_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY! = 0;
...
}
The expression is very simple, and the developer is also very good, but there are still errors. In short, I think that using parentheses in obscure code snippets is not an extra step.
I think the best solution is: if the calculation is simple and normal, you do not need to add parentheses. For example:
If (A = B & X! = Y)
If (A-B <Foo () * 2)
However, if you use an uncommon operator (~ , ^, &, |, <,> ,? :), It is best to add clear parentheses. Parentheses make the code clearer and ensure that no error occurs. For example:
If (! (A & B ))
X = A | B | (z <1? 2: 3 );
When you use uncommon operators, adding parentheses can also help you correctly use the aforementioned "? : "Operator. How to deal with "? : "Depends on your preferences. I prefer a simple method.
Summary
Write simple and clear code as much as possible. Of course, after you split long and complex expressions into multiple strings, your code length will increase. However, the Code is clearer and easier to read and understand. In addition, this code is less likely to cause errors. Do not be afraid to create additional variables. The compiler will be able to effectively optimize the code.
Do not use parentheses in expressions that contain rare operators or bits and logical operations.
In the future, programmers will be grateful only when they see your code containing parentheses.