[Debugging method] Practical debugging skills based on vs environment

Foreword:

For thousands of programmers, if there is anything more painful than writing programs in this world, it must be to find the bugs (loopholes) in the programs they write by themselves. As a novice, we often report errors in our daily code writing (good programmers just have more bugs than we have seen to reduce errors), but when we encounter errors, everyone may not understand. So much so that I worked there for a long time and finally became a "C/V" engineer. In this issue, based on the vs environment, I will lead you to understand the tips of code debugging.

insert image description here

1. What is a bug?

First of all, when we want to overcome it, we must first understand it. Just like fighting a war, only by knowing yourself and the enemy can you be victorious in all battles.

The probable reason is that once the computer broke down while writing the program. After investigation, a small moth was found in the electric relay of the computer. Failures are called "bugs". This is the origin of the "bug" we love to say today. Its meaning, consistent with the original, is really "a bedbug".
insert image description here
The specific reason can be understood:
The origin of the bug


2. What is debugging? How important is it?

Just like the police handling a case, they reason and investigate step by step according to the clues, and finally come to the final truth. Perhaps what we are most impressed with is the [Detective Conan] we have seen.A good programmer is a good detective, and every debugging is an attempt to solve a case *

For the vast majority of novice players, when we write code, it is "three times, five divisions and two". numb.
insert image description here
And how to troubleshoot the problem?
insert image description here
When debugging an error it might look like this:

insert image description here
Adding, deleting, checking and modifying in such a mindless way, you may still be stuck in place after working for half a day. Therefore, it is very important to master debugging well.


2.1 What is debugging?

Debugging (English: Debugging / Debug), also known as debugging, is a process of discovering and reducing program errors in computer programs or electronic equipment.

2.2 Basic steps of commissioning

a. Discover the existence of program errors
b. Locate the errors by means of isolation and elimination
c. Determine the cause of errors
d. Propose solutions
to correct errors e. Correct program errors and retest


2.3 Introduction to Debug and Release

Then let's take a look at the two versions under VS, namely -----Debug and Release

a:
Debug is usually called the debug version. Through the cooperation of a series of compilation options, the compiled result usually contains debugging information without any optimization, so as to provide developers with powerful application debugging capabilities and facilitate programmers to debug programs.
b:
The Release is usually called the release version, which is for users. Generally, customers are not allowed to debug on the release version. Therefore, debugging information is not saved, and at the same time, it often performs various optimizations in order to achieve the smallest code and the best speed. Provide convenience for users.

We still show it through code:

#include<stdio.h>

int main()
{
    
    
	char* p = "hello world";
	printf("%s\n", p);

	return 0;
}

When we write the above code and run it under [Debug] version
insert image description here

When we go to the file to view the information under [debug], we see the result as shown in the figure below:
insert image description here

And when our code runs under the [Release] version:

insert image description here

We can see that the file size of the same program is different under the two versions.
insert image description here

So we say that debugging is a process of finding potential problems in the code in the environment of the Debug version.

What optimizations does the compiler perform?
Please see the following code:

int main()
{
    
    
    int i = 0;
    int arr[10] = {
    
     0 };
    for (i = 0; i <= 12; i++)
    {
    
    
        arr[i] = 0;
        printf("hehe\n");
    }
    return 0;
}

It is [debug] mode to compile, the result of the program is an infinite loop
insert image description here
[release] mode to compile, the program does not have an infinite loop.

insert image description here

The difference between them is due to optimization.


3. Introduction to debugging in Windows environment

Note: The debugging tool for linux development environment is gdb, which will be introduced later in the course

3.1 Preparation for debugging environment

insert image description here
Only by selecting the debug option in the environment can the code be debugged normally.

3.2 Learn shortcut keys

insert image description here

In the picture above, I ticked some shortcut keys that are often used in daily life. Remembering the shortcut keys will greatly improve our debugging efficiency. Next, I will introduce in detail:

F5

Start debugging, often used to jump directly to the next breakpoint.

F9

Create breakpoints and cancel breakpoints
The important role of breakpoints, you can set breakpoints anywhere in the program.
In this way, the program can be stopped at any desired position, and then executed step by step.

F10

Process by process, usually used to process a process, a process can be a function call, or a statement.

F11

Statement by statement is to execute a statement each time, but this shortcut key can make our execution logic enter the function (this is the longest use).

CTRL + F5

Start execution without debugging, if you want the program to run directly without debugging, you can use it directly.

More shortcut keys can be viewed as follows:
https://blog.csdn.net/mrlisky/article/details/72622009


4. Example demonstration

Having said so much, after all, it is all on paper. Next, we will show you through specific examples.

4.1 Example 1: Sum of factorials

Code thinking:

Before we start writing code, we must think about what is the factorial sum of n?
When we have an idea in our mind, it will be very fast to write, instead of just getting started: the logic is very simple, first input n to represent the sum of the factorials of n, and finally perform the summation operation

Next, think about the knowledge you need to use in each step, as follows:

1. Factorial: 1x2x3...xn uses a loop statement

2. Summing: still using a loop

Finally, just print it out! !

code show as below:

int main()
{
    
    
	int i = 0;
	int sum = 0;//保存最终结果
	int n = 0;
	int ret = 1;//保存n的阶乘(乘法的话一定要初始化为1)
	scanf("%d", &n);

	for (i = 1; i <= n; i++)
	{
    
    
		int j = 0;
		for (j = 1; j <= i; j++)
		{
    
    
			ret *= j;
		}
		sum += ret;
	}

	printf("%d\n", sum);

	return 0;
}

Well, after having the above code, we then have to verify whether the code written at this time is correct. At the beginning, let's give a simple example, take the input [3] as an example. In our imagination, after the factorial of [3] is:

The first step: the factorial of 1, that is, 1;
the second step: the factorial of 2, that is, 1 2=2;
the third step: the factorial of 3, that is, 1
2*3=6;
the fourth step: adding three numbers, That is, 1+2+6=9 (that is, the final result is 9)

So is that really the case? Next, we will go through step-by-step debugging to see the results.

First of all, we can see that we have entered the memory loop. At this time, i = 1, j = 0, and the loop will start to execute once, so the factorial of [1] at this time can be calculated as: 1! = 1

insert image description here
After jumping out of the inner loop for the first time, we can find [1! ], so you can see that [sum] is 1 at this time

insert image description here
Then we go to calculate [2! ], at this time, inside our function, the loop operation will be performed twice. After the execution, each value at this time is shown in the following table:

insert image description here
At this point we [2! ] After the calculation is completed, the value of [ret] can be obtained, and finally the accumulation operation is performed, that is, the operation of [1+2], so the [sum] at this time should be 3, and the result is as follows:

insert image description here
After the first two executions, go back and execute [3! ] operation, three loop operations are performed internally, as we already know, [3! ] The result is 6, we continue to [F10] to see the result:

The first internal loop starts, at this time [j=1], [ret=2]

insert image description here

After the second cycle is completed, at this time [j=2], [ret=4]

insert image description here

After the third cycle, we can find that [j=3], [ret=12]

insert image description here

After the last step of the loop, we need to calculate the cumulative sum. At this time, we can find that when [j=4] we jump out of the loop operation, and the final result shows [15], which should be [9]. (Hey... how could it be [15]? At this moment, everyone's curious little heads start to agitate)

insert image description here

Don't be afraid if you encounter a program error, let's analyze it carefully. Let's sort it out and write it like this:

a:
When n=1, we enter the first cycle, and then nothing happens, enter the second cycle [ret=1*1], [sum=0+1], there is no problem at this time;
b:
When n=2, we enter the first cycle, nothing happens, and enter the second cycle, [ret=1 * 1=1], but please note that after this, [sum=sum will not be calculated +ret], but continue to not jump out in the second loop, because the condition of the second loop is [i<2], which is still true at this time, so the second loop continues, [ret=1 *2=2]; jumping out of the second loop, [sum=0+2], but please note that the first loop is not over at this time, for the first loop, [n=1] at this time, but also Continue the case of [n=2], so the final result is [4], so an error occurred at this step.

So we can change it like this (reset the value of [ret] every time), and the running result will be correct at this time:
insert image description here
Another way is that we can not use two layers of nesting for encapsulation, we only define one layer of loop, the specific code is as follows:


int main()
{
    
    
	
	int n = 1;
	scanf("%d", &n);

	int ret = 1;//保存n的阶乘(乘法的话一定要初始化为1)
	int i = 1;
	int sum = 0;//保存最终结果

	for (i = 1; i <= n; i++)
	{
    
    
		ret *= i;
		sum += ret;
	}

	printf("%d\n", sum);
	return 0;
}

5.2 Example 2: Infinite loop problem

First, I will give a piece of code, and you can guess what the program will output at the end:

int main()
{
    
    
    int i = 0;
    int arr[10] = {
    
     1,2,3,4,5,6,7,8,9,10 };
    for (i = 0; i <= 12; i++)
    {
    
    
        arr[i] = 0;
        printf("hehe\n");
    }
    return 0;
}

I believe that the first impression of most friends when they see this program is that the array subscript is [0-9], but here it is [<=12]. The obvious problem is that the array access is out of bounds

But is this really the case? The old rules run the program directly to see if it is what we think in the end.

insert image description here

Hey... We found out why the result is an endless loop of printing? Next, if you want to figure out this problem, can you analyze the reason with your naked eyes? Then you need to use debugging

At the beginning, we enter the loop, initialize the array, and get the following results.
insert image description here
Then we enter the loop, change the array elements to [0], and continue until the operation of [arr[9]]. It's within our normal range.

insert image description here

So what about the next [arr[10]]? What about [arr[11]] and [arr[12]]? What are they like? Let's continue debugging
insert image description here

From the above figure, we can find that it still operates on it, so why can we still access the position [arr[10]]? Next, I will explain to you

insert image description here
Because when our array is stored in the memory, there is a continuous storage space, and there is also a certain space after the array, and this continuous address space exists under the stack frame space of the [main] function. From its point of view, the storage spaces are all continuous, so the space after the array is also accessible.

In addition to this problem, there is another obvious problem. I don’t know if you have noticed it, that is, when we execute it to the end, the two values ​​of [i] and [arr[12]] unexpectedly become 0 at the same time. Why is this? Woolen cloth?
insert image description here
Next, we take the address of the two respectively, and we can find that the two are actually pointing to the same piece of address space
insert image description here

At this point, we can think that for the variable [i], it should be located at the last two digits of the end position of the entire array. Only in this way will the value of [i] be changed when the array is accessed out of bounds, and finally modify this block When changing the value in the block space, the value of the loop variable [i] is modified, so that the value of [i] can never reach 13, so there will be an infinite loop of printing.

Next, let's take a closer look at the memory layout.

First of all, we know that [i] and [arr] are local variable elements, and local variables are placed on the stack area in memory, and the habit of using the stack area is to use the high address space first and then the low address space (this is very Important), it can be found that the address of variable a is larger than the address of variable b.
insert image description here

So what exactly is the stack area in memory? Let's take the following picture as an example.
insert image description here
As soon as the program enters the function stack frame of the [main] function, it will first open up a space for the variable [i], and then a few positions may be vacated to open up space for ten elements in the [arr] array, according to From the above, we can find that several positions have been vacated, so why should they be vacated? (There are university questions here)

This is not stipulated by me or anyone else, the size in the middle depends on the compiler
1. Under the VC6.0 compiler, there is no extra space in the middle;
2. In gcc, the compiler under the Linux environment, create There will be an integer between local variables, that is, 4 bytes
3. In editors such as VS 2013/2019/2022, there will be two integers in the middle, that is, 8 bytes

Therefore, although this code is run on different compilers, although the phenomenon of infinite loop is obtained, the underlying implementation is somewhat different.

Then we can know that the array is from low to high during normal use, but is the address of the array also like this? Let's test it out.

insert image description here

From the above figure, we can find that the address of each element of the array changes from low to high.

With these knowledge reserves, we can look back at the original question and answer it well:

When the program starts, the variable [i] is created first, and the address space in the memory is first opened up; while the address space of the [arr] array is opened up later. However, just now we have known that the subscript of the array and the address change order of the array elements are from low to high, while the stack in the memory uses the high address first, and then the low address, so when the array is accessed backwards, It is possible to find the variable [i] and overwrite it, so it is possible to change the value of the loop variable to something else, which will cause the loop end condition to fail to be met, resulting in the phenomenon of infinite loop printing. (That's all for now)

Therefore, the correct solution is to change our loop end condition.


Summarize:

Through the study of this period, I believe that when you encounter an error in the program, you will not go directly to the program to add, delete, check and modify. You may say that this is difficult, but the saying goes well! (The best way to be afraid of fear is to conquer it)

That's all for this issue! Thank you for watching, if it is helpful to you, remember to support it three times in a row!

Guess you like

Origin blog.csdn.net/m0_56069910/article/details/129260050