[Elementary C language] Practical debugging skills (detailed introduction)

insert image description here

1. What is a bug?

The original meaning of the word Bug is "insect" or "bug"; and some undiscovered defects or problems hidden in computer systems or programs are also called "bugs". The founder of "Bug", Grace Murray Hopper (Grace Murray Hopper), is a computer expert working for the US Navy and one of the first people to integrate human language into computer programs. And the name "bug", which represents a computer program error, was taken by Heber. On September 9, 1947, after Heber programmed the Harvard Mark II with 17,000 relays, it suddenly stopped working when the technicians were running the whole machine. So they climbed up to find the reason, and found that there was a moth between the contacts of a set of relays inside the huge computer, apparently because the moth was attracted by light and heat, flew to the contacts, and then was shocked by high voltage die. So in the report, Heber used adhesive strips to stick moths, and used "bug" to mean "an error in a computer program", and the term "Bug" has been used to this day.
Corresponding to Bug, people call the process of discovering Bug and correcting it "Debug" (called "debugging" in Chinese), which means "catch bugs" or "kill bugs".

Later, bugs were used directly in many software tests to illustrate those problems.
insert image description here
The first moth to cause a computer error was discovered, and the first computer program error.

2. What is debugging? How important is it?

Everything that happens must have traces to follow. If you have a clear conscience, you don’t need to cover up and there will be no signs. If you have a guilty conscience, you must cover
up, then there must be signs. The more signs, the easier it is to follow the vine And above, this is the path of reasoning.
To go downstream in this way is crime, and to go upstream is truth.

A good programmer is a good detective.

Every debugging is the process of trying to solve the case.

  1. How do we write code?
  2. And how to troubleshoot the problem?

Deny - superstitious debugging! ! ! !

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

  1. detect the presence of programming errors
  2. Locate errors by isolating, eliminating, etc.
  3. determine the cause of the error
  4. Propose a solution to correct the error
  5. Correct program errors and retest

2.3 Introduction to Debug and Release

Debug is usually called the debug version, it contains debugging information, and does not make any optimization, which is convenient for programmers to debug programs.
Release is called a release version, which is often optimized to make the program optimal in code size and running speed, so that users can use it well.

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

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

If it is compiled in debug mode, the result of the program is an infinite loop.
If it is compiled in release mode, the program does not have an infinite loop.

So what's the difference between them?
It is because of optimization.
The order in which variables are allocated in memory has changed, which affects the results of program execution.

3. Introduction to debugging in Windows environment

Note: The linux development environment debugging tool is gdb

3.1 Preparation for debugging environment

Only by selecting the debug option in the environment can the code be debugged normally.

3.2 Learn shortcut keys

The most commonly used shortcut keys:
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.

3.3 View the current information of the program during debugging

3.3.1 View the value of a temporary variable

Used to observe the value of a variable after debugging has started.

3.3.2 View memory information

insert image description here

3.3.3 View call stack

Through the call stack, the call relationship of the function and the location of the current call can be clearly reflected.

insert image description here

3.3.4 View assembly information

insert image description here

After the debugging starts, there are two ways to go to assembly:
(1) The first method: right-click the mouse, and select [Go to disassembly]:
(2) The second method: you can switch to the assembly code.

3.3.5 View register information

You can view the usage information of the registers in the current operating environment.
insert image description here

4. A lot of hands-on, try to debug, in order to make progress

  1. Must be proficient in debugging skills.
  2. Beginners may spend 80% of their time writing code and 20% of their time debugging. But a programmer may spend 20% of his time writing programs, but 80% of his time debugging.
  3. We're all talking about some simple debugging.
  4. In the future, there may be very complex debugging scenarios: debugging of multi-threaded programs, etc.
    Use more shortcut keys to improve efficiency.

5. Some debugging examples

5.1 Example 1

Implementation code: seek 1! +2! +3! ...+ n! ; overflow is not considered.

int main()
{
    
    
	int i = 0;
	int sum = 0;//保存最终结果
	int n = 0;
	int ret = 1;//保存n的阶乘
	scanf("%d", &n);  //举一个1到3的阶乘,1到3,1!+2!+3!=1 + 2 + 6 =9但编译器结果为15
	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;
}

Give a factorial of 1 to 3, 1 to 3, 1! +2! +3! =1 + 2 + 6 =9 but the compiler result is 15.
At this time, if we are 3, we expect to output 9, but the actual output is 15.

Why?
Here we have to ask our questions.

  1. First speculate on the cause of the problem. It is best to initially determine the probable cause of the problem.
  2. In fact, manual debugging is necessary.
  3. We know when we debug
int main()
{
    
    
	int i = 0;
	int sum = 0;//保存最终结果
	int n = 0;
	int ret = 1;//保存n的阶乘
	scanf("%d", &n); 
	for (i = 1; i <= n; i++)
	{
    
    
		int j = 0;
		ret = 1;//经过调试,这样改就对了
		for (j = 1; j <= i; j++)
		{
    
    
			ret *= j;
		}
		sum += ret;
	}
	printf("%d\n", sum);
	return 0;
}

5.2 Example 2

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

Research the reasons for the infinite loop of the program
insert image description here
:

  1. i and arr are local variables, and local variables are placed on the stack area.
  2. The habit of using the memory in the stack area is to use the space at the high address first, then use the space at the low address, and then use the space at the low address.
  3. As the subscript increases in the array, the address changes from low to high.

insert image description here

insert image description here
Note:
The result of running this code is related to the environment.

6. How to write good (easy to debug) code

6.1 Good code

  1. the code works fine
  2. few bugs
  3. efficient
  4. high readability
  5. High maintainability
  6. Note clearly
  7. complete documentation

Common coding skills:

  1. use assert
  2. Try to use const
  3. Develop a good coding style
  4. Add necessary comments
  5. Avoid coding pitfalls

6.2 Demonstration using the simulation library function strcpy

Simulate implementation of library functions: strcpy

The library function strcpy itself looks like

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char arr1[] = "hello world";
	char arr2[20] = {
    
     0 };
	strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

Simulate implementation of library functions: strcpy

//模拟实现库函数:strcpy
//strcpy
// string copy
//字符串拷贝
//1
void my_strcpy(char* dest, char* src)
{
    
    
	while (*src != '\0')
	{
    
    
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;  //\0的拷贝
}
int main()
{
    
    
	char arr1[] = "hello world";
	char arr2[20] = {
    
     0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);

	return 0;
}

Picture explanation:
insert image description here
optimize the code:

//2
void my_strcpy(char* dest, char* src)
{
    
    
	while (*src != '\0')
	{
    
    
		*dest++ = *src++;
	
	}
	*dest = *src;  //\0的拷贝
}
int main()
{
    
    
	char arr1[] = "hello world";
	char arr2[20] = {
    
     0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);

	return 0;
}

//3
void my_strcpy(char* dest, char* src)
{
    
    
	while (*dest++ = *src++)
	{
    
    
		;
	}
	//此时不需要\0的拷贝
}
int main()
{
    
    
	char arr1[] = "hello world";
	char arr2[20] = {
    
     0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);

	return 0;
}
//4
#include <assert.h>
void my_strcpy(char* dest, char* src)
{
    
    
	//断言-对程序员自己是一件非常好的习惯,出错误会告诉在哪里
	//需要包含头文件<assert.h>
	assert(dest != NULL);
	assert(src != NULL);
	//or   assert( dest && src );
	while (*dest++ = *src++)
	{
    
    
		;
	}
}
int main()
{
    
    
	char arr1[] = "hello world";
	char arr2[20] = {
    
     0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);

	return 0;
}

Notice:

  1. Design of analysis parameters (name, type), design of return value type
  2. Here we explain the dangers of wild pointers and null pointers.
  3. The use of assert, here is the role of assert
  4. The use of const in the parameter part, here is an explanation of the role of const modified pointers
  5. Note addition

6.3 The role of const

int main()
{
    
    
	//int n = 10;
	//n = 20;
	int n = 100;
	const int m = 0;
	//m = 20;//err
	//const 修饰指针
	//1. const 放在*的左边, *p不能改了,也就是p指向的内容,不能通过p来改变了。但是p是可以改变的,p可以指向其他的变量
	//2. const 放在*的右边,限制的是p,p不能改变,但是p指向的内容*p,是可以通过p来改变的
	//
	const int * p = &m;
	*p = 20;//err
	p = &n;//ok

	int* const p = &m;
	*p = 20;//ok
	p = &n;//err
	printf("%d\n", m);

	return 0;
}

Conclusion:
When const modifies pointer variables:

  1. const is placed on the left of *, *p cannot be changed, that is, the content pointed to by p cannot be changed through p. But p can be changed, p can point to other variables
  2. const is placed on the right, restricting p, p cannot be changed, but the content p pointed to by p can be changed through p

Diagram:
const is placed on the left side of *
insert image description here
const is placed on the right side of *
insert image description here
const is placed on both sides of *
insert image description here
Do you know why this means?

Looking back:

#include <assert.h>
void my_strcpy(char* dest, const char* src)
{
    
    
	//断言-对程序员自己是一件非常好的习惯,出错误会告诉在哪里
	//需要包含头文件<assert.h>
	assert(dest != NULL);
	assert(src != NULL);
	//or   assert( dest && src );
	while (*src++ = *dest++)  //加const后,如果在打代码的过程中,交换对象写反后会产生报错
	{
    
    
		;
	}
}
int main()
{
    
    
	char arr1[] = "hello world";
	char arr2[20] = {
    
     0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);

	return 0;
}

After adding const, if the exchange object is reversed, an error will be reported during the process of typing the code, which will improve the robustness of the code.

The error reminder is as follows:
insert image description here
optimize the code:

//strcpy函数返回的是目标空间的起始地址

char* my_strcpy(char* dest, const char* src)
{
    
    
	//断言 - 保证指针的有效性
	assert(dest && src);
	char* ret = dest;
	//把src指向的字符串拷贝到dest指向是的数组空间,包括\0字符
	while (*dest++ = *src++)
	{
    
    
		;
	}
	return ret;
}

int main()
{
    
    
	char arr1[] = "hello world";
	char arr2[20] = {
    
     0 };
	//链式访问
	printf("%s\n", my_strcpy(arr2, arr1));

	return 0;
}

Exercise:
Simulate the implementation of a strlen function
library function strcpy itself

#include <string.h>
int main()
{
    
    
	int len = strlen("abc");
	printf("%d\n", len);
	return 0;
}

Simulation implementation:

int my_strlen(char* str)
{
    
    
	int count = 0;
	while (*str != '\0')
	{
    
    
		count++;
		str++;
	}
	return count;
}
int main()
{
    
    
	int len = my_strlen("abc");
	printf("%d\n", len);
	return 0;
}

From the above content, directly optimize

#include <stdio.h>
#include <assert.h>

int my_strlen(const char* str)
{
    
    
	int count = 0;
	//assert(str != NULL);
	assert(str);
	//while (*str != '\0')
	while (*str)
	{
    
    
		count++;
		str++;
	}
	return count;
}

int main()
{
    
    
	int len = my_strlen("abc");
	printf("%d\n", len);
	return 0;
}

7. Common errors in programming

7.1 Compilation errors

ctrl+f - search

Look directly at the error message (double-click) to solve the problem. Or it can be done with experience. Relatively simple.

7.2 Linked errors

Look at the error message, mainly find the identifier in the error message in the code, and then locate the problem. Usually the identifier name does not exist or is misspelled.

7.3 Runtime errors

With the help of debugging, locate the problem step by step. The most difficult.

Reminder:
Be a caring person and accumulate experience in troubleshooting.
Key points of explanation:
Introduce how each error occurs and how to solve it after it occurs.

If this blog is helpful to everyone, I hope you give Hengchuan a free like as encouragement, and comment and bookmark it, thank you! ! !
It is not easy to make, if you have any questions or give Heng Chuan's opinion, welcome to leave a message in the comment area.

Guess you like

Origin blog.csdn.net/m0_75058342/article/details/129592040