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

数组

    • 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 扫雷

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python自动化测试ConfigParser模块读写配置文件

Python自动化测试ConfigParser模块读写配置文件 ConfigParser 是Python自带的模块&#xff0c; 用来读写配置文件&#xff0c; 用法及其简单。 直接上代码&#xff0c;不解释&#xff0c;不多说。 配置文件的格式是&#xff1a; []包含的叫section, section 下有optionval…

Mac Pro 突然不能双击打开文件夹

当Mac Pro 突然不能双击打开文件夹 不防右击看看这儿 有没有勾选 如果勾选就会在打开的瞬间 闪退关掉文件夹

安全SCDN详解

随着互联网发展&#xff0c;越来越多的人都会使用到网站&#xff0c;个人网站、企业网站&#xff0c;各类大大小小的网站影响到人们的生活方方面面。许多站长都有使用或是听起过CDN&#xff0c;而SCDN&#xff0c;全称为“Security Content Delivery Network”&#xff0c;即安…

【2024】vue-router和pinia的配置使用

目录 vue-routerpiniavue-routerpinia进阶用法---动态路由 有同学在项目初始化后没有下载vue-router和pinia&#xff0c;下面开始&#xff1a; vue-router npm install vue-router然后在src目录下创建文件夹router&#xff0c;以及下面的index.ts文件&#xff1a; 写进下面的…

如何选择阿里云服务器配置,过来人说说

阿里云服务器配置怎么选择&#xff1f;CPU内存、公网带宽和系统盘怎么选择&#xff1f;个人开发者或中小企业选择轻量应用服务器、ECS经济型e实例&#xff0c;企业用户选择ECS通用算力型u1云服务器、ECS计算型c7、通用型g7云服务器&#xff0c;阿里云服务器网aliyunfuwuqi.com整…

京津冀国际光伏展

京津冀国际光伏展是中国在京津冀地区举办的一项光伏展览活动。光伏&#xff0c;即太阳能光伏发电技术&#xff0c;是利用太阳能将光能转化为电能的一种可再生能源技术。光伏展旨在促进光伏产业的发展与合作&#xff0c;推动光伏技术在京津冀地区的应用和推广。 在京津冀国际光伏…

对VisionPro的认识,CogPMAlingTool模板匹配工具练习

什么是VisionPro&#xff1f; 在认识VisionPro之前我们需要先熟悉一下图片的各种格式 这里我们可以参考来自githubcurry博主的文章 图片各种格式的区别以及计算机如何存储图片 VisionPro 是由世界领先的机器视觉公司 Cognex 开发的一款专业机器视觉软件。它提供了强大的图像…

Vue3使用ckeditor5,加载mathtype组件,并将最终结果转化为latex格式输出

vue3项目直接用vite搭建就行。我用的是4.1.0版本的vite 接下来说一下要注意的几个点&#xff1a; 1、ckeritor的相关依赖安装必须版本相同&#xff0c;不然会报依赖重复的错误 主要有以下依赖&#xff1a; "ckeditor/ckeditor5-basic-styles": "^38.0.1"…

基于springboot实现的幼儿园管理系统

一、系统架构 前端&#xff1a;html | layui | jquery | css 后端&#xff1a;springboot | mybatis 环境&#xff1a;jdk1.8 | mysql | maven 二、代码及数据库 三、功能介绍 01. 登录页 02. 系统管理-用户管理 03. 系统管理-页面管理 04. 系统管理-角色管…

进制算法题(进制转换、Alice和Bob的爱恨情仇)

进制的本质 对于一个十进制数字&#xff0c;比如说153&#xff0c;其本质是每一个数位上的数字乘上这一位上的权重&#xff0c;即:153(1x)(5x)(3 x)而二进制&#xff0c;只不过是把10换成了2&#xff0c;任意一个非负整数都有唯一的一个二进制表示: 在计算机中&#xff0c;数字…

视频推拉流EasyDSS平台直播通道重连无法转推的原因排查与解决

视频推拉流EasyDSS视频直播点播平台&#xff0c;集视频直播、点播、转码、管理、录像、检索、时移回看等功能于一体&#xff0c;可提供音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务。 用户使用EasyDSS平台对直播通道进行转推&#xff0c;发现只要关闭…

Codesys自定义库的帮助文档的美化

文章目录 1.前言2.美化的方式2.1.利用html标签2.2.利用reStructuredText 3.相关说明3.1.使用reStructuredText时&#xff0c;中文注释的问题3.2.将文档需要的图片包含到库中3.3.文档的作用区域 1.前言 当我们在codesys中写好自己的库&#xff0c;并且发布给别人使用时&#xf…

vue基本用法

文本插值 {{}} 用来绑定data方法返回的对象属性 v-bind:为标签的属性绑定data方法中返回的属性 事件绑定v-on:xxx 简写为xxx 双向绑定v-model 条件渲染 v-if v-else v-else-if 动态渲染页面元素

数字政府建设中的锐捷力量:五维构建坚实的数字政务基础设施

3月1日,中国信息协会部分地方信息机构负责人会议暨信息服务业助力高质量发展研讨会在深圳成功召开。来自民政部、农业农村部、国家统计局、人民日报社等部委单位,全国省市信息协会、信息中心、大数据局负责人,信息化领域专家学者在内的230多名代表参加了会议。2024年是立足“二…

C# SwinV2 Stable Diffusion 提示词反推 Onnx Demo

目录 介绍 效果 模型信息 项目 代码 下载 C# SwinV2 Stable Diffusion 提示词反推 Onnx Demo 介绍 模型出处github地址&#xff1a;https://github.com/SmilingWolf/SW-CV-ModelZoo 模型下载地址&#xff1a;https://huggingface.co/SmilingWolf/wd-v1-4-swinv2-tagg…

【开源物联网平台】FastBee使用EMQX5.0接入步骤

​&#x1f308; 个人主页&#xff1a;帐篷Li &#x1f525; 系列专栏&#xff1a;FastBee物联网开源项目 &#x1f4aa;&#x1f3fb; 专注于简单&#xff0c;易用&#xff0c;可拓展&#xff0c;低成本商业化的AIOT物联网解决方案 目录 一、将java内置mqtt broker切换成EMQX5…

电商之战:实时监控竞争对手战术,掌握市场先机!

淘宝天猫、拼多多、抖音、小红书等国内平台&#xff0c;甚至包括亚马逊、速卖通等跨境商家&#xff0c;在竞争如此激烈的电商平台&#xff0c;想要脱颖而出&#xff0c;打造店铺的差异化运营&#xff0c;通过对竞争对手甚至选品的监控可以更好地了解市场趋势和变化。 这有助于…

Jmeter正则表达式提取器

伙伴们是否遇到过以下的场景&#xff1a; 响应报文类似下面的这样 我们要使用phrase后面的其中一个值。 使用正则表达式提取后匹配出多少值&#xff0c;提取结果如下&#xff1a; 现在的问题是&#xff0c;如果我们要使用正则表达式提取后的&#xff1a;使用其中的第1个和第1…

前端H5动态背景登录页面(上)

最近一段时间看一些关于前端的东西&#xff0c;下面分享两个非常不错的前端动态背景登陆页面&#xff0c;还有几个等后面有时间了再整理。 1、彩色气泡登录页面 下面是源代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8…

互联网智慧工地源码,“互联网+建筑大数据”SaaS微服务架构,支持PC端、手机端、数据大屏端

智慧工地源码&#xff0c;支持多端展示&#xff08;PC端、手机端、平板端&#xff09;SaaS微服务架构&#xff0c;项目监管端&#xff0c;工地管理端源码 智能时代的风暴已经融入了我们生活的每个方面&#xff0c;智能手机、iPad等移动终端智能设备已经成为我们生活的必需品。智…