c语言入门---调试技巧

2023-11-12

什么是bug?

程序错误,即英文的Bug,也称为缺陷、臭虫,是指在软件运行中因为程序本身有错误而造成的功能不正常、死机、数据丢失、非正常中断等现象。 早期的计算机由于体积非常庞大,有些小虫子可能会钻入机器内部,造成计算机工作失灵。1947 年 9 月 9 日,赫柏及其团队发现了第一个 Bug。当天下午 3 点 45 分,赫柏在哈佛的 Mark II 电脑的日志簿上记录下了“第一个电脑故障”。问题的根源是一只飞蛾卡在了电脑的继电器触点之间,赫柏及时地把这只飞蛾粘在了 Mark II 的日志上,并用双关语写道:“第一次发现了真正的 Bug。”( “First actual case of bug being found.” )这个 Bug 其实是被其他人发现的,但是赫柏在日志上做了记录。从此以后,人们将计算机错误称为Bug,与之相对应,人们将发现Bug并加以纠正的过程叫做“Debug”,意即“捉虫子”或“杀虫子”。那么这个图片就是当年的bug:请添加图片描述

调试是什么?调试的基本步骤是什么?

调试是什么?

调试又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程。

调试的基本步骤是什么?

那么我们的调试的过程分为这么几个部分,首先是我们程序员自己发现错误,我们在运行的代码的过程中编译器会自动帮助我们识别一些错误,那么这时我们程序员自己就会寻找这些错误出现的原因,来一步一步的修改我们的代码,那么这就是我们调试的第一步,那么我们程序员也不是万能的,有些错误可能藏的比较隐蔽他没发现,那么这时就到了我们的测试人员上场了,他们就会用专门的软件,根据公司对这个程序的要求来进行专业的测试来发现这个代码中的bug,那么发现错误之后就会返回给我们的程序让其对其进行修改,这样发现错误修改错误,一直到测试部门通过就发行出来给我们的用户使用,但是我们用户在使用的时候也会发现出来一些bug,比如说我们平时打的一些游戏是不是bug满天飞啊,比如说我玩的战地5那简直就是神仙打击,所以我们用户也会发现一些新的bug,那么这些bug发现出来之后,我们就会返还给我们的程序员,程序员就会根据这个bug来找到错误的原因,并且找到解决这个bug的方法,然后再进行测试再发行新的版本,然后再找到bug这样反反复复,比如说我们的王者荣耀出了这么多年了,更新了那么多次,但是依然会出现新的bug这是同样的道理,那么我们这里消除错误的方法一般都是以隔离的形式来对错误进行定位,因为我们的错误的出现一般都是模块的形式,不可能说整个写的代码都有问题,可能一个问题就出现在一个函数里面等等,比如说我们后面学的数据结构,我们就是采用函数的形式对其进行分装,那么我们以后发现错误的时候就可以对这些函数进行单独的测试来一个一个的排查我们的问题,那么这个就是我们的调试的过程。

Debug和release的区别

我们的同一段代码在运行的时候是可以产生两种不同的条件的,一个是Debug版本另外一个就是release版本,比如说我们这个代码:

#include<stdio.h>
int main()
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", i);
	}
	return 0;
}

那么我们这里首先在Debug的版本下运行一下:请添加图片描述
我们可以看到在对应的路径下面出现了这么一个Debug文件:
请添加图片描述
我们点进Debug文件夹就可以看到下面的这个场景:
请添加图片描述
那么我们这里就有一个应用程序,那么这个就是我们Debug条件下所创建的大小为39KB,那么这里我们可以将我们的Debug改成我们的release版本我们再来看看会发生什么样的情况:请添加图片描述
我们发现这里多了一个Release的文件夹,那么我们这里的点进去看也有一个可执行的程序:
请添加图片描述
但是在这个可执行程序跟我们上面的那个就有那么点点的不同,因为我们这里的大小要比我们上面的小很多,上面那个是39KB而我们这个只有9KB,所以单从这一点我们就可以看到我们这里的两个版本不仅仅只有名字上的不同,也会有一些细节方面上的不同,那么我们这里就来简单的介绍:
Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户最好地使用。
那么看了上面的介绍想必大家应该能够明白我们上面的两个可执行程序为什么大小上会有区别了,那么这里有小伙伴们就要说了,那么我们在平时写代码的时候是不是都使用release版本啊,因为这个执行速度更快啊,那么我们这里答案是不是的哈,虽然我们这里运行的速度快,并且内存小但是我们这个release版本他是无法进行调试的,而调试这个功能对我们程序员来说是非常重要的,比如说我们刚刚写的那个代码:
我们先用Debug版本调试一下:请添加图片描述
我们发现这个代码它可以一步一步的来进行调试,比如说我们这里的循环它循环的是十次,那么我们这里每次循环的过程我们都可以一一的观测出来,但是我们这里release版本就不会它会一下子跳过这整个循环,就像这样:请添加图片描述
所以这就是我们这里的一个主要的区别,所以说Debug版本是我们程序员用的版本,而我们的用户用的就是我们的release版本。

windows的调试介绍

调试的准备

那么这里的调试的准备就很简单我们只用将我们的版本改成Debug版本就行。

调试的操作

1.F5

启动调试,经常用来跳转到我们的下一个断点处。那么这句话是什么意思呢?我们可以来看一下我们上面的代码我们直接按一下F5看看会发生什么:请添加图片描述
我们发现我们这里直接打印出结果然后就直接结束了调试,那么这里出现这种的情况是因为我们没有设置断点,如果我们设置断点的话我们这里按个F5他就会自动跳到我们的断点出,等待着下一步的调试,那么我们这里是如何来设置我们的断点的呢?那么这里我们就只需要在你想要断点的那一行处按一下F9就可以了在这里设置一个断点,那么这时我们再按一下F5我们看看会有啥情况:请添加图片描述
我们发现这里的箭头直接指向了我们这里的断点处,那么这里我们就可以执行其他的操作,那么这里我们再按一下F5的话,我们这里就会直接跳到下一个断点处,那么我们这里的下面没有断点了,那么是不是就应该直接程序编译结束了呢?答案是并不是,虽然我们这里看的我们这里没有断点,但是我们这里是存在逻辑上的断点的,所以我们再按一次F5也不会直接退出调试的,我们可以看看这个代码:

#include<stdio.h>
int main()
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", i);
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", i);
	}
	return 0;
}

在这里插入图片描述
我们按了一次F5我们的箭头理所当然的跑到我们这里的第一个断点处,此时我们可以通过监视来看到此时的i的值还是0,请添加图片描述
但是我们这里再按一下F5的话,我们这里会发现这里的箭头依然是在这个位置没有发生更改:
请添加图片描述
但是我们这里通过监视就可以看到我们这里的i却变成了1,那么这里就更可以说明了一件事就是我们这里的F5跳跃的是逻辑上的断点,并不是我们看到的断点,那么这里的逻辑上的断点就是这样的:请添加图片描述
我们得将这个循环拆开,往里面添加断点,这里的断点就是我们所说的逻辑上的断点,那么这里固然就存在了一个问题就是,要是我们在调试的时候知道这个循环中的前几次循环是对的,但是我要是设置断点的话还是得经历这些本就是对的循环经历,那么这里是不是就有点麻烦啊,所以在我们c语言当中我们是可以给这个断点添加条件的,右击断点就可以然后就会出现这样的窗口请添加图片描述
那么这里我们就可以给这个断点来设置条件,那么我们就设置当i=4的时候这个断点才生效,那么我们这里按一下F5来看看会变成什么样子:
请添加图片描述
我们这里直接就打印出来了0 1 2 3 ,并且我们这边的调试显示的也是i=4请添加图片描述
那么我们这里可以给我们的断点加上条件的话,那么我们在调试的时候就会方便很多。

2.F9

那么根据我们上面的介绍就可以知道我们这里的F9是用来设置断点的。

3.F10

那么相对于我们的F5我们的F10就没有那么的快我们的F10他是逐个过程的调试,一个过程可以是一次函数的调用也可以是一条语句

4.F11

逐语句,就是每次都执行一条语句,但是这个快捷键能够使得我们的执行过程进入函数的内部。比如说我们下面的代码:

#include<stdio.h>
int ADD(int x, int y)
{
	int z = x + y;
	return z;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = ADD(a, b);
	return 0;
}

那么我们这里可以对他进行调试首先我们用F10来进行调试:
请添加图片描述
但是我们这里的调试的情况是直接跳过了我们这里的这个函数的调用,所以如果我们想要进入这个函数的内部的话,我们就得在此处按F11来进入我们函数的内部,那么我们这里可以看一下:
请添加图片描述
我们要是按F11的话我们这里就可以直接进入我们的函数内部。

调试的时候查看程序当前的信息

那么这里我们就来讲讲如何来查看我们程序的信息,那么这里我们下面的操作都是得在你开始调试之后才能够看到,这里大家注意一下。

自动窗口

请添加图片描述
那么我们自动窗口的打开的路径就是在调试开始后点击上方的调试的按钮然后鼠标划到窗口,然后你就可以看到有自动窗口这个选项,然后点击进去我们就出现了这样的情况:请添加图片描述
那么我们这里的自动窗口就会自动根据程序调试的进度来判断你可能会观察的值,那么这里就出现了一种情况就是我们要是想观察其他的值呢?比如说我们这里在函数调用之前我们可以看到这里甚至连b的值都没有显示出来,然后我们再进入函数看看:请添加图片描述
我们发现这里的值就只剩我们这里函数内部的值了,之前的值我们都观察不到了,那么这里就是我们的一个缺点,编译器太自作主张了自动改变我们要观察变量,所以这个窗口我们用的并不多。

局部变量

那么我们这里还有一个选着就是局部变量,那么这个窗口的特点就是直接加载我们这个程序中的的全部的变量请添加图片描述
大家可以看到我们这里的程序虽然只运行到了开头,但是我们这里调试部分已经把我们这里当前代码块中的所有变量全部都显示了出来,然后我们这里可以再通过按F11进入这个函数中来看看这个我们这里的监视会发生什么样的变化:
请添加图片描述
我们可以发现我们这里刚进入函数的内部,我们这里的监视的内容就发生了变化,变成了当前函数内部的全部变量的值,所以通过上面的操作我们可以看到我们这里局部变量的作用就是加载当前的全部变量。

监视

那么我们这里的监视相对于上面的两个功能则更加的实用一些,因为我们这里的监视是你想要观察谁就可以观察谁,我们上面的那两个功能里面所观察的值都是编译器自己给你的,你无法做出修改但是我们这里的监视你就可以在你观察的值里面进行修改,比如说上面的a的值,我们确实可以通过上面的那两个功能来看它的值是多少,但是我要是想观察这个a的值加上这个b的值呢?我要是想观察一个表达式的结果呢?我要是想判断一个关系表达式的结果是真还是假呢?那么这些功能我们的监视都可以实现,比如说下面的代码:

#include<stdio.h>
int ADD(int x, int y)
{
	int z = x + y;
	return z;
}
int main()
{
	int a = 10;
	int b = 20;
	if (a < b)
	{
		int c = ADD(a, b);
	}
	return 0;
}

那么我们这里的监视就可以这样做请添加图片描述
我们这里就可以任意的对我们想要观察的值附加一些条件上去,这样我们观察的值也就会更加的丰富一点,然后我们这里在进入函数内部之后这些值也不会消失,这样就可以更加的方便我们的观察:
请添加图片描述

内存

那么我们程序在跑起来的时候肯定会在内存中存取一些有用值上去,那么我们这里就可以通过内存这个选项来观察我们这里内存中的值,那么我们这里观察的样子就是这样的:
请添加图片描述
那么我们这里内存中观察的值就是这样的,我们这里讲上面的列改成了4,这样的话我们这里每一行观察到的值就是一个字节,那么我们这里就在上面输入你想要观察的地址,然后我们这里就会自动的跳转到你输入的地址处,如果你不知道该变量的地址,你可以这么输入:请添加图片描述
然后再敲击回车这样的话他就会自动的找到这个变量a所对应的地址
请添加图片描述
那么我们内存中存储的实际上是二进制,但是我们这里编译器为了方便大家观察,就把这个二进制转化成十六进制,那么我们这里还可以看到我们这个数据的后面还有一些奇奇怪怪的符号,那么这个符号的就是编译器根据我们这里内存中的数据自行推导出来的一些可能表示的符号,所以这个用处不大大家不必关心。

调用堆栈

我们在写函数的时候总是会出现这样的情况就是就是一个函数的内部调用另外的一个函数,结果这个函数的内部又调用另外的一个函数,那么很多时候这个逻辑是非常的复杂的不利于我们的观察,那么这时就有了我们调用堆栈这个功能,那么这个功能就可以在调试开始的时候看到我们这里函数调用的逻辑:

#include<stdio.h>
void test3()
{
	;
}
void test2()
{
	test3();
}
void test1()
{
	test2;
}
int main()
{
	test1();
	return 0;
}

那么我们这里就可以看一下我们这里的函数堆栈中的逻辑:
请添加图片描述
那么我们这里就可以看到我们这里函数调用的逻辑,我们这里调用的顺序就是从下往上,那么我们这里就是先调用我们的main函数在调用我们的test1函数,再调用我们的test2函数再调用我们的test3函数,那么根据我们之前的学习我们就知道一件事情就是我们这里的main函数也是通过外部调用得到的,那么我们这里可以右击鼠标把显示外部代码这个选项勾上就可以显示我们这里完整的函数调用的过程:
请添加图片描述

寄存器

那么根据我们之前的学习我们知道在程序运行的时候会有这么一个东西就是寄存器,他会存放一些十分特殊的值,那么我们怎么来观察各个寄存器中的值呢?那么这里我们就可以在那窗口里面找到这些寄存器这个选项:
请添加图片描述
那么点开之后我们就会出现这样的一个窗口,那么这里就显示的是我们所有寄存器中存储的值,我们程序在跑的时候我们的寄存器就会存入不同的值,那么我们就可以通过这个窗口来观察我们的不同地方寄存器中的不同的值。当然如果你要是知道我们寄存器的名字的话也是可以通过监视来观察我们这里寄存器的值的。

反汇编

如果有时候你想要观察一个代码的底层逻辑的话我们这里就可以通过反汇编来讲这些语言改成汇编语言来观察他们的底层逻辑,那么如何来反汇编呢?那么这里我们就可以直接在代码处点击鼠标的右键,然后就会出现一堆的选项,然后我们就可以在其中找到我们其中的一个反汇编这个选项,点击这个选项你就会看到这样的景象:
请添加图片描述
那么这里就是我们的汇编代码。

实例一

我们可以来看一下下面的这个代码

#include<stdio.h>
int main()
{
	int i = 0;
	int sum = 0;
	int n = 0;
	int ret = 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;
}

那么我们这个代码的作用就是计算我们这里的1!+2!+3!…+n!,但是我们这里在运行的时候就会发现我们这里计算是有错误的,我们这里输入了一个3,但是我们这里运行输出的结构却是15,我们可以看一下:
请添加图片描述
那么这里是什么原因呢?我们这里发现了错误那么我们这里就可以尝试一下通过调试来看看这个问题出现的原因:请添加图片描述
那么我们这里首先输入了一个3,此时我们的各个数据都没有问题,那么我们就进入循环请添加图片描述
、进入循环之后我们就将我们的j初始化为了0,然后我们就进入了内部的循环,那么这个内部循环的作用就是计算我们的阶乘,那么我们这里的i一开始是1,所以我们这里的内部循环就只会进入一次,然后就跳出了循环,那么这个时候我们就会将我们的值加给我们的sum请添加图片描述
那么此时我们的sum的值就是1,然后我们第一次循环就结束了,我们的i就变成了2,小于我们这里的n,然后我们就再进入这里的内部循环,因为我们这里的i等于2,所以我们这里的内部循环就会循环两次,请添加图片描述
我们可以看到我们这里的ret是等于1的,并且我们的j也等于1 ,然后我们这里循环了两次我们的结果就成了这样:请添加图片描述
我们的ret的值等于2,那么这里也是对的因为我们的2的阶乘确实是等于2 的,那么我们再将这个值加给我们的sum,那么此时我们的sum的值就应该等于3请添加图片描述
那么我们这里可以看到我们这里的值确实是等于3的,然后我们的i就等于了3就会再循环一遍,那么这里就会再次进入我们内部的循环请添加图片描述
那么这里我们就来计算的3的阶乘,那么这里我们就将j的值初始化为1,开始计算我们3的阶乘,我们数学中的阶乘的计算是从1开始一直往后面乘的,但是我们仔细观察的话就会发现我们这里的ret是2啊,那么是2的话我们这里的阶乘就变成了从2,开始往后面乘了,那么这样的话我们这里就必然会出现错误啊,所以我们将这个内部循环算完之后就可以发现我们这里的ret的值就变成了12请添加图片描述
所以这就是我们这里问题存在的关键,我们在第三次循环的时候我们一开始ret的值就变成了2,并不是1所以这就是我们的问题所在,那么为了解决这个问题我们就可以直接在每次进入内部循环之前加上一段代码,将我们的ret的值初始化为1,就可以解决我们这里的问题:

#include<stdio.h>
int main()
{
	int i = 0;
	int sum = 0;
	int n = 0;
	int ret = 1;
	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;
}

实例二

#include<stdio.h>
int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i <= 12; i++)
	{
		arr[i] = 1![请添加图片描述](https://img-blog.csdnimg.cn/e4f91e3ee7ae4c8488605bf16ecc6434.png)
;
		printf("hehe\n");
	}
	return 0;
}

那么我们再来看看这个代码,我们这里创建了一个10个元素的数组,然后我们就进入了一个循环这个循环会循环13次,每次都将arr[ i ]的值赋值为0,并且打印一个hehe那么这里我们就可以将这个代码运行一下:
请添加图片描述
那么我们这里就出现了一个问题,我们这里出现了死循环,那么这是为什么呢?我们可以看看代码发现我们这里的arr数组在进行赋值的时候会出现越界访问的情况,那么我们这里的问题应该就出现在我们这里的调试部分,那么我们这里就可以通过调试的方法来看一下到底是为什么:
请添加图片描述
那么这里是我们一开始各个变量对应的值,我们知道循环10次是不会出现什么问题的,那么我们这里直接循环11请添加图片描述
并且也打印了11个hehe出来请添加图片描述
那么我们这里就再来往下走循环第12次
请添加图片描述
我们发现我们这里i的值变成了11,我们的arr[11]的值也变成了1,那么我们这里还是没有发现什么异常,那么我们再走一步我们这里就会将arr[12]的值变成1并且打印一个hehe,那么再走一步
请添加图片描述
我们发现我们上面确实打印出来了一个hehe但是我们这里i的值也发生了变化请添加图片描述
我们i的值确实变成了12,但是我们再执行完arr[12]=1之后,我们这里的i的值也发生了变化,请添加图片描述
我们这里i的值也变成了1,那也就是我们arr[12]对应的位置的值就是i ,那么这里的i变成1的话我们就又会进入这个循环,然后我们就又会对原来初始化过的位置的值再进行一次初始化,那么初始化到最后又会把i赋值成1,那么这里就是我们问题出现的关键,因为我们知道在栈区中内存的使用是从高地址往低地址使用的,而我们数组中随着数组下标的增长,我们对应的空间的地址的是在逐渐变高的,所以我们这里越界访问的话就会出现把我们i的值也改变的情况,所以这就是我们问题出现的关键所在。

编程常见类型的错误

编译型的错误(语法错误)

那么这个错误就非常的简单,就是你的语法出现了错误,比如说你把int a写成了itn a这就是一个语法的错误,还比如说你写的时候分号没有加啊,或者说写了一个中文的分号上去,那么这些都是错误所在,那么我们这样的错误就叫编译型的错误。这种错误编译器都会给你报出了告诉你在哪个位置出了错误。-

链接型的错误

那么这个错误出现的原因很可能就在于你使用了一个函数,但是这个函数你是没有实现的,所以就会报出这样的错误来,比如说下面的代码:

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int c = add(a, b);
	return 0;
}

我们这里使用这个add函数但是我们并没有实现这个函数,那么我们在运行这个代码的时候就会报出这样的错误请添加图片描述
这里的LNK就是链接型错误的标志,大家以后写代码的时候可以注意一下。

运行时的错误

那么上面两种错误都是比较简单的,但是我们这个错误就比较的隐蔽他不会报错,得我们自己一步一步的调试来找到错误的所在,那么这个就只能靠大家慢慢的积累了。
点击此处获取代码

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c语言入门---调试技巧 的相关文章

  • 找环

    文章目录 找环 1 算法分析 1 1 判断是否存在环 1 2 找出所有环上的所有点 1 3 类拓扑排序找出与度数有关的点集 gt 退化到度数为2的环 1 4 找到以s为源点的最小环 1 5 floyd找到有向图 无向图的最小环 2 模板 2
  • 神秘时代服务器信息没汉化,[聊天

    Server thread ERROR Could not load plugins Msgreplace1 0 jar in folder plugins org bukkit plugin InvalidPluginException
  • Qt界面小技巧之设置不同模块不同颜色

    文章目录 一 效果图 二 使用场景 三 使用方法及分析 3 1 步骤 3 2 分析 总结 一 效果图 下图为不同模块不同颜色的效果图 下方每一个字符都是独立的QLabel控件 模块对应的颜色码 1 rgb 170 85 255 2 rgb
  • 日志异常检测初步实践与探索

    1 背景 日志的主要目的是记录系统 包括服务和业务等 状态和重要的事件帮助定位系统的问题 日志对于理解系统状态和定位性能问题至关重要 因此 日志是在线监控和异常检测的一个重要信息源 在很多业务和服务的故障自愈过程中 日志异常检测与根因分析是

随机推荐

  • 人工智能3d建模算法_从2D到3D的跨越,康云全球首创“人工智能AI+3D自动化建模与展示技术”...

    康云 人工智能AI 3D自动化建模与展示技术 第八届中国创新创业大赛 广东赛区 暨第七届 珠江天使杯 科技创新创业大赛 获奖项目巡礼系列报道八 你还在使用传统的3D建模技术 传统3D建模 模型制作成本高耗时耗力 模型体积大不方便传输 传播
  • 【DirectX12】3.配置FBX_SDK

    配置FBX SDK 1 介绍 FBX格式是现在最主流的用于游戏的3D模型格式 要使用DirectX12绘制模型 当然需要先用这个库来解析模型数据 它的版权协议如下 不是开源的并且是非商用的 假设你完成了一个引擎到时候还需要联系他们讨论版权的
  • 巨头的联盟链选择

    FISCO BCOS是完全开源的联盟区块链底层技术平台 由金融区块链合作联盟 深圳 简称金链盟 成立开源工作组通力打造 开源工作组成员包括博彦科技 华为 深证通 神州数码 四方精创 腾讯 微众银行 亦笔科技和越秀金科等金链盟成员机构 代码仓
  • leetcode-每日一题2022.4.16 最大回文数乘积

    题目 力扣 思路 一个n位数的范围在10 n 1 到10 n 1之间 两个n位数的乘积的位数是2n或2n 1位 除了n 1以外 最大回文整数的位数都是2n位 从大到小枚举每一个可能的回文整数 判断它是否能用两个n位数的乘积来表示 回文整数可
  • ESP32开发-HTTP POST请求发送图片

    HTTP POST请求发送图片 ESP32中有HTTP相关的请求demo 但是demo中只是简单的HTTP header加上body 如果服务器对POST请求有要求 比如数据以key value的形式传输 那么服务器会无法正常处理请求 从而
  • 在 Linux 系统中查找 MAC 地址的方法

    参考文章 多种方法在Linux上找到MAC地址 引言 MAC Media Access Control 地址是网络设备 如网卡 的唯一标识符 用于在局域网中进行通信 在 Linux 系统中 有几种方法可以查找设备的 MAC 地址 在本文中
  • 线性代数(六)——二次型

    文章目录 前言 二次型是什么 二次型的表示 合同矩阵与合同二次型 正定二次型 正定矩阵 二次型的题型 前言 一直对二次型和线性代数的关系不解 导致一系列的知识点因为没有理解而常常忘记 在这里对二次型进行梳理 希望可以加深对二次型的印象 二次
  • 【软件测试】POST请求包含哪些参数

    POST请求包含哪些参数 HTTP 协议是以 ASCII 码传输 建立在 TCP IP 协议之上的应用层规范 规范把 HTTP 请求分为三个部分 请求行 状态行 URL 请求头 Headers 请求体 消息主体 Body 协议规定 POST
  • 容器编排学习(一)容器技术

    一 容器 1 Linux 容器的起源 容器的起源可以追溯到1979年 UNIX 系统中提供的chroot命令 容器的最初的设计目标是为了隔离计算机中的各类资源以便降低软件开发 测试阶段的风险 或者充当蜜罐 吸引黑客的攻击 以便监视黑客的行为
  • 计算机组成原理笔记

    CPU的功能和结构 运算器 对数据进行加工 算术逻辑单元ALU 暂存寄存器 通用寄存器 累加寄存器ACC 程序状态字寄存器PSW 移位器 计数器 控制器 取指令 分析指令 执行指令 中断处理 程序计数器PC 指令寄存器IR 指令译码器 时序
  • 第53步 深度学习图像识别:Bottleneck Transformer建模(Pytorch)

    基于WIN10的64位系统演示 一 写在前面 1 Bottleneck Transformer Bottleneck Transformer 简称 BotNet 是一种深度学习模型 在2021年由Google的研究人员在论文 Bottlen
  • 【Mysql】数据库第三讲(表的约束、基本查询语句)

    表的约束和基本查询 1 表的约束 1 1 空属性 1 2默认值 1 3列描述 1 4 zerofill 1 5主键 1 6 自增长 1 7 唯一键 1 8外键 1 表的约束 真正约束字段的是数据类型 但是数据类型约束很单一 需要有一些额外的
  • 腾讯应用宝米大师直购模式支付流程以及服务端php回调校验

    一 米大师简介 1 米大师 Midas 为腾讯官方唯一虚拟支付平台 2 YSDK已经内置了米大师 Midas 支付模块 游戏开发者接入YSDK后 可以通过相应的配置开启米大师的支付功能 并调用YSDK已经封装好的接口 即可快速实现支付能力
  • windows 10 下安装labelme出错

    请大件帮我看看问题在哪 谢了
  • MongoDB的身份验证

    1 当开启了安全检查之后 只有通过身份认证的用户才能进行数据的读写操作 2 admin和local是两个特殊的数据库 它们当中的用户可对任何数据库进行操作 3 经认证后 管理员用户可对任何数据库进行读写操作 同时能执行只有管理员才能执行的命
  • 关于进制(十进制,二进制,八进制,十六进制)

    进制 十进制 在日常生活中使用 满10进1 计数 0 1 2 3 4 5 6 7 8 9 10 11 12 18 19 20 单位数字有10个 0 9 二进制 满2进1 计数 0 1 10 11 100 101 110 111 单位数字有2
  • 新版来啦|ShardingSphere 5.4.0 重磅发布

    Apache ShardingSphere 本周迎来了 5 4 0 版本的发布 该版本历时两个月 共合并了来自全球的团队和个人累计 1271 个 PR 新版本在功能 性能 测试 文档 示例等方面都进行了大量的优化 本次更新包含了不少能够提升
  • CUDA向量加法示例

    CUDA向量相加示例 贺志国 下面以向量加法为例 介绍CUDA实现代码 以下是具体代码vector add cu 我的版本除CUDA相关函数外 其他代码均以C 的方式实现 并且尽可能调用STL库的数据结构和算法 注意 CUDA核函数内部的日
  • delphi 获取有输入焦点的活动窗口信息

    var wintext array 0 MAXBYTE of Char WdChar array of Char focuswhd THandle processId Pointer threadid Cardinal GUITHREADI
  • c语言入门---调试技巧

    目录 什么是bug 调试是什么 调试的基本步骤是什么 调试是什么 调试的基本步骤是什么 Debug和release的区别 windows的调试介绍 调试的准备 调试的操作 1 F5 2 F9 3 F10 4 F11 调试的时候查看程序当前的