C语言数组全面解析:从初学到精通

news2024/9/28 15:32:05

数组

    • 1. 前言
    • 2. 一维数组的创建和初始化
    • 3. 一维数组的使用
    • 4. 一维数组在内存中的存储
    • 5. 二维数组的创建和初始化
    • 6. 二维数组的使用
    • 7. 二维数组在内存中的存储
    • 8. 数组越界
    • 9. 数组作为函数参数
    • 10. 综合练习
      • 10.1 用函数初始化,逆置,打印整型数组
      • 10.2 交换两个整型数组
      • 10.3 三子棋
      • 10.4 扫雷

在这里插入图片描述

1. 前言

大家好,我是努力学习游泳的鱼。今天我们来学习数组。
在这里插入图片描述
数组分为一维数组和二维数组,我们需要学习它们如何创建和初始化,如何使用,如何在内存中存储。话不多说,让我们开始对数组的学习吧。

2. 一维数组的创建和初始化

数组是一组相同类型元素的集合。

一维数组的创建格式如下:
数组的元素类型 数组名[常量表达式];
int arr[100]; // 表示数组能存放100个int类型的数据
我们使用大括号来初始化数组。
完全初始化:对数组的所有元素都进行初始化。大括号里初始化的元素个数和数组最多存放的元素个数相同。
int arr[10] = {1,2,3,4,5,6,7,8,9,10}; // 完全初始化:数组最多存放10个元素,大括号内初始化了10个元素
不完全初始化:只初始化数组的部分元素,剩下的被初始化成0
int arr[100] = {0}; // 不完全初始化:第一个元素被手动初始化成0,剩下的元素默认被初始化为0
如果对数组初始化,数组大小可以省略,默认为初始化元素的个数。如下面两种写法效果是相同的。

int arr[5] = {1,2,3,4,5};
int arr[] = {1,2,3,4,5}; // 省略数组大小,默认是5

如果创建数组时不初始化,则数组大小不能省略,此时分为两种情况:

  1. 局部数组里存放的都是随机值。
  2. 全局数组会被默认初始化为0
int arr1[10]; // 默认初始化为0

int main()
{
	int arr2[10]; // 存储的是随机值

	return 0;
}

对于一个变量,这两种情况也成立。

  1. 当一个局部变量不初始化时,存储的是随机值。
  2. 当一个全局变量或静态变量不手动初始化时,会被默认初始化成0
int a; // 默认初始化为0

int main()
{
	static int b; // 默认初始化为0

	int c; // 存储的是随机值

	return 0;
}

本质上,这是存储位置的差异导致的。局部变量是存储在栈区的,栈区上的数据如果不初始化,存储的是随机值。全局变量和静态变量是存储在静态区的,静态区上的数据如果不手动初始化,会被默认初始化为0

C99中引入了变长数组的概念,允许数组的大小用变量来指定,如果编译器不支持C99中的变长数组,那就不能使用。

int n = 10;
int arr[n]; // 变长数组

变长数组不能初始化。

int n = 10;
int arr[n] = {0}; // 不能像这样初始化

3. 一维数组的使用

数组通过下标来访问,数组的下标是0开始的。
[]是下标引用操作符。

比如,int arr[] = {10,20,30,40,50};,10的下标是020的下标是130的下标是240的下标是350的下标是4
那么,arr[3]对应的就是40,如果我们想把40改成400,就这么写:arr[3] = 400;
我们如何计算数组的元素个数呢?很简单,用数组的总大小除以数组一个元素的大小就行了。比如对于arr数组,数组元素个数sz就可以这么算:int sz = sizeof(arr) / sizeof(arr[0]);
假设我们想存储1~100的整数并打印出来,就可以这么写:

#include <stdio.h>

int main()
{
	int arr[100] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	int i = 0;
	for (; i < sz; ++i)
	{
		arr[i] = i + 1;
	}

	for (i = 0; i < sz; ++i)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

4. 一维数组在内存中的存储

一维数组在内存中是连续存放的。
随着数组下标的增长,地址是由低到高变化的。

我们可以写个程序来验证。

#include <stdio.h>

int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	int i = 0;
	for (; i < sz; ++i)
	{
		printf("&arr[%d] = %p\n", i, &arr[i]);
	}

	return 0;
}

我们把数组元素的地址按照下标从低到高打印出来。
在这里插入图片描述

我们发现,相邻两个元素之间的地址都差4,这是因为数组在内存中是连续存放的,相邻元素的地址就差一个int,即4个字节。
并且,随着下标的增长,地址是由低到高变化的。

5. 二维数组的创建和初始化

如我们想要创建一个三行五列的二维整型数组,就可以这么写:int arr[3][5];这个数组有三行五列,共15个元素,每个元素是int类型的。
int int int int int
int int int int int
int int int int int
对二维数组进行初始化要使用大括号,初始化时会一行一行放,一行放满后才会放下一行。若是不完全初始化,剩余元素会被默认初始化成0
如:int arr[3][5] = { 1,2,3,4,5,6 };的效果是:
1 2 3 4 5
6 0 0 0 0
0 0 0 0 0
由于二维数组的每一行都可以看作一个一维数组,如果要对每一行分别初始化,可以在大括号里使用大括号。
这么说有点抽象,举个例子:int arr[3][5] = { {1,2}, {3,4}, {5,6} };的效果是:
1 2 0 0 0
3 4 0 0 0
5 6 0 0 0
如果对二维数组初始化,二维数组的行可以省略,但是列不能省略。如果省略二维数组的行,编译器会根据初始化的内容来确定有几行。
如:int arr[][5] = {1,2,3,4,5,6};的效果是:
1 2 3 4 5
6 0 0 0 0
由于两行就够放了,编译器会默认行数为2
再比如:int arr[][5] = { {1,2}, {3,4}, {5,6} };的效果是:
1 2 0 0 0
3 4 0 0 0
5 6 0 0 0
由于3行就够放了,编译器会默认行数为3
如果我们想给数组的所有元素都初始化成0,就可以这么写:
int arr[3][5] = {0};数组的第一个元素被初始化成0,剩下的元素会被默认初始化成0,效果是,数组的全部元素都被初始化成0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

6. 二维数组的使用

二维数组也是用下标来访问的。和一维数组的区别是,二维数组有两个下标,行标和列标。行标和列标都是从0开始的。比如说,一个35列的数组arr,每个位置的元素的访问方式如下:
arr[0][0] arr[0][1] arr[0][2] arr[0][3] arr[0][4]
arr[1][0] arr[1][1] arr[1][2] arr[1][3] arr[1][4]
arr[2][0] arr[2][1] arr[2][2] arr[2][3] arr[2][4]
如果我们想把这些元素打印出来,可以用一层循环产生行标,另一层循环产生列标。每行打印完后,记得换个行。

#include <stdio.h>

int main()
{
	int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7} };

	int i = 0;
	for (; i < 3; ++i)
	{
		int j = 0;
		for (; j < 5; ++j)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}

	return 0;
}

当然,二维数组有几行几列也是可以通过代码计算出来的。
计算行数的方法:sizeof(arr) / sizeof(arr[0]);即数组的总大小除以数组第一行元素的大小。
计算列数的方法:sizeof(arr[0]) / sizeof(arr[0][0]);即数组第一行元素的大小除以数组第一行第一列的元素的大小。
上面的代码就可这样改进:

#include <stdio.h>

int main()
{
	int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7} };

	int i = 0;
	for (; i < sizeof(arr) / sizeof(arr[0]); ++i)
	{
		int j = 0;
		for (; j < sizeof(arr[0]) / sizeof(arr[0][0]); ++j)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}

	return 0;
}

7. 二维数组在内存中的存储

我们用下面的程序把二维数组每个元素的地址打印出来。

#include <stdio.h>

int main()
{
	int arr[3][5] = { 0 };

	int i = 0;
	for (; i < sizeof(arr) / sizeof(arr[0]); ++i)
	{
		int j = 0;
		for (; j < sizeof(arr[0]) / sizeof(arr[0][0]); ++j)
		{
			printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
		}
	}

	return 0;
}

在这里插入图片描述
我们发现,每两个元素之间都差4个字节,每当发生换行,如第一行最后一个元素arr[0][4]到第二行第一个元素arr[1][0]也差4个字节。arr是一个整型数组,每两个元素之间差1int类型的大小。这说明:

二维数组在内存中也是连续存放的。
随着下标的增长,地址也是由低到高变化的。

除此之外,我们还可以把二维数组的每个元素理解成一维数组。如:对于三行五列的二维数组arrarr[0]就是这个二维数组的第一个元素,同时arr[0]也是一个有五个元素的一维数组,这五个元素分别是arr[0][0]arr[0][1]arr[0][2]arr[0][3]arr[0][4]

8. 数组越界

数组的下标是有范围限制的。
数组的下标规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1
所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以程序员写代码时,最好自己做越界的检查。
如下面的程序,当i10时就越界了。

int main()
{
	int arr[10] = {0};
	int i = 0;
	for (; i<=10; ++i)
	{
		arr[i] = i;
	}
	
	return 0;
}

二维数组的行和列也可能存在越界。

9. 数组作为函数参数

数组名表示什么?看看下面的代码:

#include <stdio.h>

int main()
{
	int arr[10] = { 0 };

	printf("arr = %p\n", arr);
	printf("arr + 1 = %p\n", arr + 1);

	printf("&arr[0] = %p\n", &arr[0]);
	printf("&arr[0] + 1 = %p\n", &arr[0] + 1);

	printf("&arr = %p\n", &arr);
	printf("&arr + 1 = %p\n", &arr + 1);

	printf("sizeof(arr) = %d\n", sizeof(arr));

	return 0;
}

运行结果:
在这里插入图片描述
由运行结果可知,数组名(arr)和数组首元素地址(&arr[0])是完全相同的,且+1之后也是完全相同的,都跳过了4个字节(1int)。数组的地址(&arr)和前两者值是相同的,但+1之后跳过了40个字节(即跳过了整个数组),这是因为,如果写arr&arr[0]都表示首元素地址,即第一个整型元素的地址,+1会跳过一个整型;如果写&arr,取出的是整个数组的地址,+1自然也跳过了整个数组。注意:以上所有计算都是十六进制的计算,如所谓的跳过40个字节其实是+0x28,再转换成十进制后得到的40
并且,sizeof(arr)计算的是整个数组的大小,即40

数组名一般表示数组首元素的地址。但是有两个例外:

  1. sizeof(数组名),数组名不是数组首元素的地址,数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,数组名不是数组首元素的地址,数组名表示整个数组,取出的是整个数组的地址。

题目:写一个函数,对一个整型数组进行冒泡排序。

冒泡排序:对数组中2个相邻的元素进行比较,如果不满足就交换。

比如,一个数组里存放的是
9 8 7 6 5 4 3 2 1 0
我想把它排成升序,也就是左边的数要比右边的数小。
首先比较98,因为98大,不满足升序,就交换。
8 9 7 6 5 4 3 2 1 0
接着比较97,因为97大,不满足升序,就交换。
8 7 9 6 5 4 3 2 1 0
接着比较96,因为96大,不满足升序,就交换。
8 7 6 9 5 4 3 2 1 0
……
这一趟冒泡排序下来,数组就排序成
8 7 6 5 4 3 2 1 0 9
9的位置就正确了。接下来进行下一趟冒泡排序。由于9的位置处在正确的位置上,下一趟冒泡排序就不需要考虑9了,只需排序除了9之外的数字。
首先比较87,因为87大,不满足升序,就交换。
7 8 6 5 4 3 2 1 0 9
接着比较86,因为86大,不满足升序,就交换。
7 6 8 5 4 3 2 1 0 9
……
这一趟冒泡排序下来,数组就排序成
7 6 5 4 3 2 1 0 8 9
8的位置就正确了。
有没有发现,一趟冒泡排序,可以使一个数字的位置正确。那么,假设有10个数字,总共需要几趟冒泡排序呢?答案:10个数字需要9趟冒泡排序。因为一趟冒泡排序搞定一个数字,9趟冒泡排序就搞定9个数字,最后一个数字的位置自然也是正确的。
那么一趟内部又需要多少次比较呢?假设有10个数字,第一趟就需要比较9次,第一趟结束后就搞定了其中一个数字,只需排序剩下9个数字,所以第二趟需要比较8次。由于每趟排序都能够搞定一个数字,每趟排序都会比上一趟少一次比较,这样就知道每一趟需要几次比较了。
有了以上的分析,我们需要写两层循环。外层循环负责控制冒泡排序的趟数,假设数组有sz个元素,就需要排序sz-1趟;内层循环控制一趟内部冒泡排序的比较,第一趟需要sz-1次比较,第二趟需要sz-2次比较,后面每趟都比前一趟少一次比较,如果外层循环的循环变量(假设是i)是从0开始的,那第i趟就需要sz-1-i次比较。
如果一趟比较完后没有发生交换,则数组就已经有序了,就不需要继续排序了。我们可以定义一个flag并初始化为1,假设数组已经有序。如果发生交换,则把flag改成0。如果一趟冒泡排序中没有发生交换,则flag仍然是1,就已经有序了,不需要继续排序了。
数组元素个数怎么知道呢?一定要在数组创建的局部范围内计算数组的元素个数int sz = sizeof(arr) / sizeof(arr[0]);因为如果传参给别的函数后,我们使用数组名传参,由前面的知识可知,数组名表示数组首元素的地址,传递过去的是一个指针,所以sizeof(arr)就是4或者8sizeof(arr) / sizeof(arr[0])这个表达式是无法计算出数组元素个数的。

#include <stdio.h>

void bubble_sort(int arr[], int sz)
{
	int i = 0;
	for (; i < sz - 1; ++i)
	{
		// 一趟冒泡排序
		int flag = 1; // 假设已经有序
		int j = 0;
		for (; j < sz - 1 - i; ++j)
		{
			if (arr[j] > arr[j + 1])
			{
				flag = 0;
				// 交换
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}

		if (1 == flag)
			return;
	}
}

int main()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	// 冒泡排序
	bubble_sort(arr, sz);

	int i = 0;
	for (; i < sz; ++i)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

当然,就一种情况是无法代表所有场景的。我写了个测试代码,在不同场景下测试这个冒泡排序是否正确,供大家参考。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int test_bubble_sort()
{
	srand((unsigned int)time(NULL));

	const int sz = 100; // 数组元素个数
	int* arr = (int*)malloc(sz * sizeof(int));
	if (NULL == arr)
	{
		perror("test_bubblesort::malloc");
		return 0;
	}

	int i = 0;
	for (; i < 1000; ++i)
	{
		// 生成随机数组
		int j = 0;
		for (; j < sz; ++j)
		{
			arr[j] = rand();
		}

		// 冒泡排序
		bubble_sort(arr, sz);

		// 检验排序是否成功
		for (j = 0; j < sz - 1; ++j)
		{
			if (arr[j] > arr[j + 1])
			{
				free(arr);
				return 0; // 排序失败
			}
		}
	}

	free(arr);
	return 1; // 检验成功
}

int main()
{
	int ret = test_bubble_sort();
	if (1 == ret)
		printf("检验成功\n");
	else
		printf("检验失败\n");

	return 0;
}

10. 综合练习

10.1 用函数初始化,逆置,打印整型数组

初始化和打印应该相当简单了吧,只需要产生所有的下标,遍历数组,就能够访问整个数组并初始化或打印了。唯一需要注意的是,整型数组无法在函数内部使用sizeof(arr) / sizeof(arr[0])计算元素个数,因为数组作为函数参数传递时,传递的是首元素的地址。
初始化函数(假设初始化为全0):

void init(int arr[], int sz)
{
	int i = 0;
	for (; i < sz; ++i)
	{
		arr[i] = 0;
	}
}

打印函数:

#include <stdio.h>

void print(int arr[], int sz)
{
	int i = 0;
	for (; i < sz; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

接下来讲讲如何逆置。类似字符串的逆置,整型数组的逆置只需交换第一个元素和最后一个元素,再交换第二个元素和倒数第二个元素,接着交换第三个元素和倒数第三个元素……
如果用下标来访问数组,我们需要一个左下标left指向第一个元素,一个右下标right指向最后一个元素,交换后,left向后走,right向前走,当left还在right左边时,说明还有元素可以交换,否则就跳出循环。

void reverse(int arr[], int sz)
{
	int left = 0;
	int right = sz - 1;

	while (left < right)
	{
		int tmp = arr[left];
		arr[left] = arr[right];
		arr[right] = tmp;
		++left;
		--right;
	}
}

10.2 交换两个整型数组

如何交换两个整型数组arr1arr2(假设两个数组一样大)呢?
很简单,只需要遍历两个数组,把对应位置的元素交换就行了!

#include <stdio.h>

int main()
{
	int arr1[] = { 1,3,5,7,9 };
	int arr2[] = { 2,4,6,8,0 };
	int i = 0;
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	printf("交换前:\n");
	printf("arr1: ");
	for (i = 0; i < sz; ++i)
	{
		printf("%d ", arr1[i]);
	}
	printf("\narr2: ");
	for (i = 0; i < sz; ++i)
	{
		printf("%d ", arr2[i]);
	}
	printf("\n");

	// 交换arr1和arr2
	for (i = 0; i < sz; ++i)
	{
		int tmp = arr1[i];
		arr1[i] = arr2[i];
		arr2[i] = tmp;
	}

	printf("交换后:\n");
	printf("arr1: ");
	for (i = 0; i < sz; ++i)
	{
		printf("%d ", arr1[i]);
	}
	printf("\narr2: ");
	for (i = 0; i < sz; ++i)
	{
		printf("%d ", arr2[i]);
	}
	printf("\n");

	return 0;
}

10.3 三子棋

我们可以用已有的知识,实现一个三子棋小游戏。详细讲解戳这里

10.4 扫雷

再来实现一个扫雷小游戏。详细讲解戳这里

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1485896.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[计算机网络]--I/O多路转接之poll和epoll

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、poll函…

python复盘(1)

1、变量名的命名规则 #3、变量名的命名规则&#xff1a;可以用中文作为变量名&#xff1b;其他和go语言一样 # 变量名可以用数字、字母、下划线组成&#xff0c;但是数字不能作为开头 # 变量名不能使用空格&#xff0c;不能使用函数名或关键字 # 变量名最好能表示出他的作用2、…

【PyQt】16-剪切板的使用

文章目录 前言一、代码疑惑快捷键 二、现象2.1 复制粘贴文本复制粘贴 2.2 复制粘贴图片复制粘贴 2.3 复制粘贴网页 总结 前言 1、剪切板的使用 2、pycharm的编译快捷键 3、类的属性和普通变量的关系 4、pyqt应该养成的编程习惯-体现在代码里了&#xff0c;自己看看。 一、代码…

springboot+vue学生信息管理系统学籍 成绩 选课 奖惩,奖学金缴费idea maven mysql

技术栈 ide工具&#xff1a;IDEA 或者eclipse 编程语言: java 数据库: mysql5.7 框架&#xff1a;ssmspringboot都有 前端&#xff1a;vue.jsElementUI 详细技术&#xff1a;springbootSSMvueMYSQLMAVEN 数据库工具&#xff1a;Navicat/SQLyog都可以学生信息管理系统主要实现角…

源码视角,vue3为什么推荐用ref,而不是reactive

ref 和 reactive 是 Vue3 中实现响应式数据的核心 API。ref 用于包装基本数据类型&#xff0c;而 reactive 用于处理对象和数组。尽管 reactive 似乎更适合处理对象&#xff0c;但 Vue3 官方文档更推荐使用 ref。 我的想法&#xff0c;ref就是比reactive好用&#xff0c;官方也…

JAVA读取局域网电脑文件全流程

JAVA读取局域网电脑文件全流程 需求设计实现1、创建非微软用户&#xff08;1&#xff09;win11 不可达电脑开启网络共享2、设置文件夹共享3、高级共享设置打开文件夹与打印机共享3、java编码 需求 需要读取内网一台电脑中的文件并解析数据&#xff0c;但机器不可接入办公网&am…

京东云硬钢阿里云:承诺再低10%

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 阿里云刚刚宣布史上最大规模的全线产品降价20%&#xff0c;这热度还没过&#xff0c;京东云当晚就喊话&#xff1a;“随便降、比到底!&#xff0c;全网比价&#xff0c;击穿低价&#xff0c;再低10%”&#xff0c;并…

贪心(基础算法)--- 区间选点

905. 区间选点 思路 &#xff08;贪心&#xff09;O(nlogn) 根据右端点排序 将区间按右端点排序 遍历区间&#xff0c;如果当前区间左端点不包含在前一个区间中&#xff0c;则选取新区间&#xff0c;所选点个数加1&#xff0c;更新当前区间右端点。如果包含&#xff0c;则跳…

蓝桥OJ 2942数字王国之军训排队 DFS剪枝

蓝桥OJ 2942数字王国之军训排队 #include<bits/stdc.h> using namespace std;const int N 15;//最多10队 int a[N], n; vector<int>v[N];//二维数组 v[i]记录队伍i中所有人的编号bool dfs(int cnt, int dep) {if (dep n1){//判断合法性for (int i 1; i < n; …

OpenAI划时代大模型——文本生成视频模型Sora作品欣赏(十三)

Sora介绍 Sora是一个能以文本描述生成视频的人工智能模型&#xff0c;由美国人工智能研究机构OpenAI开发。 Sora这一名称源于日文“空”&#xff08;そら sora&#xff09;&#xff0c;即天空之意&#xff0c;以示其无限的创造潜力。其背后的技术是在OpenAI的文本到图像生成模…

docker 转为docker-compose(composerize 命令)

可以使用Composerize将Docker命令转换为Docker Compose文件。 例如&#xff1a;将docker run命令转换为Docker Compose格式&#xff0c;只需用Composerize运行它&#xff0c;如下所示&#xff1a; composerize docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/…

Kubernetes: 本地部署dashboard

本篇文章主要是介绍如何在本地部署kubernetes dashboard, 部署环境是mac m2 下载dashboard.yaml 官网release地址: kubernetes/dashboard/releases 本篇文章下载的是kubernetes-dashboard-v2.7.0的版本&#xff0c;通过wget命令下载到本地: wget https://raw.githubusercont…

错误笔记:Anaconda 错误(闪退、无法安装等) + Pycharm 错误(无法启动)+ python 报错

Anaconda 错误 1、导航器启动中发生-- 闪退 方法一&#xff1a; Windows下&#xff1a; 1&#xff09;使用管理员运行&#xff1a;conda prompt 2&#xff09;执行命令 conda update anaconda-navigator 方法二&#xff1a; 重置Anaconda配置&#xff1a;anaconda-navigator…

基于C语言实现内存型数据库(kv存储)

基于C语言实现内存型数据库(kv存储) 文章目录 基于C语言实现内存型数据库(kv存储)1. 项目背景1.1 Redis介绍1.2 项目预期及基本架构 2. 服务端原理及代码框架2.1 网络数据回环的实现2.2 array的实现2.3 rbtree的实现2.4 btree的实现2.5 hash的实现2.6 dhash的实现2.7 skiplist的…

【设计模式】观察者模式及函数式编程的替代C++

本文介绍观察者模式以及使用函数式编程替代简单的策略模式。 观察者模式 观察者模式是一种行为型设计模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;其所有依赖者都会收到通知并自动更新。 当对象间存在一对多关系时&#…

flutter 文字一行显示,超出换行

因为app有多语言&#xff0c;中文和其他语言长度不一致&#xff0c;可能导致英文会很长。 中文样式 英文样式 代码 Row(mainAxisAlignment: MainAxisAlignment.end,crossAxisAlignment: CrossAxisAlignment.end,children: [Visibility(visible: controller.info.fee ! null,ch…

更详细的软件测试理论基础:流程,开发、测试模型,测试分类,测试用例及其设计方法,缺陷

文章目录 一、测试流程二、开发模型1、 瀑布模型2、增量模型3、快速模型4、其他 三、测试模型1、V模型2、W模型 四、测试分类五、测试用例 test case六、测试用例设计方法1、等价类划分法2、边界值分析法3、因果图法4、判定表法5、正交法6、场景法7、流程分析法8、错误推测法方…

Apipost自动化测试持续集成配置方法

安装 Apipost-cli npm install -g apipost-cli 运行脚本 安装好Apipost-cli后&#xff0c;在命令行输入生成的命令&#xff0c;即可执行测试用例&#xff0c;运行完成后会展示测试进度并生成测试报告。 Jenkins配置 Apipost cli基于Node js运行 需要在jenkins上配置NodeJs依…

基于springboot+vue的流浪动物救助网站(源码+论文)

目录 前言 一、功能实现 1 普通员工 1.1普通员工登录 1.2圈子信息 1.3志愿者 1.4求助 1.5资源 1.6寄养 2 系统管理员 2.1管理员登录 2.2圈子信息 2.3志愿者 2.4 求助 2.5 资源 2.6 领养 2.7个人信息 二、库表设计 三、论文 前言 随着生活的加快以及互联网时…

Mybatis_plus-逻辑删除、通用枚举、自动填充、插件等

一、逻辑删除 曾经我们写的删除代码都是物理删除。 逻辑删除&#xff1a;删除转变为更新 ​ update user set deleted1 where id 1 and deleted0 查找: 追加 where 条件过滤掉已删除数据,如果使用 wrapper.entity 生成的 where 条件也会自动追加该字段 ​ 查找: select id,nam…