第四站:数组

news2025/7/8 12:04:55

目录

一、一维数组的创建和初始化

1.数组的创建

(1)基本定义,创建方式

(2)经典的错误标准的零分

2.数组的初始化

3.一维数组的使用

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

二、二维数组的创建和初始化

1.二维数组的创建

 2.二维数组的初始化

3.二维数组的使用

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

 三、数组越界

四、数组作为函数参数

1.关于冒泡排序的经典错误标准零分

2.数组名到底是什么呢

(1)一般的,数组名是首元素地址

(2)数组不是首元素地址的两个特例

第一个特例,sizeof(数组名)

第二个特例,&数组名

(3)总结

(4)&数组名和直接使用数组名的区别

3.再次研究冒泡排序

总结


一、一维数组的创建和初始化

1.数组的创建

(1)基本定义,创建方式

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

数组的创建方式:type_t   arr_name   [const_n];

//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小
举例:
int arr1[10];
char arr2[5+6];
double arr3[10];

(2)经典的错误标准的零分

如下代码所示,不满足常量表达式,因为n是一个变量。

#include<stdio.h>
int main()
{
	int n = 10;
	int arr[n];
	return 0;
}
注: 数组创建,在 C99 标准之前, [] 中要给一个 常量 才可以,不能使用变量。在 C99 标准支持了变长数组的概念,数组的大小可以使用变量指定,但是数组不能初始化。
我们可以在linux 上的gcc编译器上使用变长数组,这个编译器是支持C99标准的

2.数组的初始化

下面是数组的初始化的各种方式以及区别

这是整型数组

#include<stdio.h>
int main()
{
	//创建的同时给数组一些值,这叫初始化
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};//完全初始化
	int arr2[10] = { 1,2,3 };//不完全初始化,剩余的元素默认初始化为0
	int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };//这里没有指定数组元素个数,编译会根据初始化的内容来确定数组的元素个数

	int arr4[] = { 1,2,3 };//3个元素
	int arr5[10] = { 1,2,3 };//10个元素

}

 这是字符数组

#include<stdio.h>
int main()
{
	//arr6与arr7一模一样都是有3个元素,并且为a,b,c
	char arr6[3] = { 'a', 'b', 'c' };
	char arr7[] = { 'a', 'b', 'c' };
	
	char arr8[10] = "abc";//十个元素,前三个为abc,后面都为\0
	char arr9[] = "abc";//四个元素,前三个为abc,最后一个为\0

}

还有一种就是全局数组不初始化默认为0,局部数组不初始化为随机值,这一点与全局变量不初始化默认为0,局部变量不初始化为随机值是类似的。

下面是调用监视窗口查看全局数组a1和局部数组a的内容。

 还有这两个初始化一定要搞清楚他们的区别

#include<stdio.h>
int main()
{
	char arr8[] = "abc";
	char arr9[] = {'a', 'b', 'c'};

	printf("%s\n", arr8);
	printf("%s\n", arr9);

}

arr8中有\0,arr9没有\0

运行结果为

3.一维数组的使用

 在之前我们提到过一个下标引用操作符 [ ],他其实就是数组访问的操作符。

1.数组都是由下标的,下标是从0开始的

2.[]下标引用操作符

下面是顺序打印数组的方法

#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i =0 ; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

下面是倒序打印数组,跳着打印数组

#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i =0 ; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	for (i = sz-1; i >=0; i--)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	for (i = 0; i < sz; i=i+2)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

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

我们看这样一个代码
#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);

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

结果为

我们发现每两个地址间差4,而我们每个int类型的元素是4个字节,所以我们得出以下结论
1.一维数组在内存中是连续存放的
2.随着数组下标的增长,地址是由低到高变化的
我们画一个图理解一下

 

因此我们只需要得到一个数组元素的地址,我们就可以顺藤摸瓜得到所有的数组元素以及他们的地址
按照这个思路,我们可以这样打印出我们的数组元素
#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = &arr[0];
	for (i = 0 ; i < sz; i++)
	{
		printf("arr[%d]=%d\n", i, *(p+i));
	}
	return 0;
}

运行结果为

 我们也可以试一下这个代码,验证一下指针+i的地址与&arr[i]的地址

#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = &arr[0];
	for (i = 0 ; i < sz; i++)
	{
		printf("%p -----%p\n", p+i, &arr[i]);
	}
	return 0;
}

运行结果为

 可见两个地址是一样的

二、二维数组的创建和初始化

1.二维数组的创建

int arr[3][4];
char arr[3][5];
double arr[2][4];
我们这样理解二维数组,比如第一个就是由三行和四列的元素,共12个,也就是说第一个控制行,第二个控制列

 2.二维数组的初始化

我们看这个代码
#include<stdio.h>
int main()
{
	int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	return 0;
}

我们调试进行监视, 可以得到他们的分布

我们再试试没有放满的

#include<stdio.h>
int main()
{
	int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int arr2[3][4] = { 1,2,3,4,5 };

	return 0;
}
监视内部

 我们在换一种初始化方式

#include<stdio.h>
int main()
{
	int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int arr2[3][4] = { 1,2,3,4,5 };
	int arr3[3][4] = { {1,2} ,{3,4}, {5,6} };

	return 0;
}
内部为
 二维数组初始化时候的行是可以省略的,列不可以省略
我们看下面的初始化例子

或者我们改变列为2,则为

3.二维数组的使用

二维数组的使用也是通过下标的方式
比如我想打印出二维数组arr4的每个元素,代码实现如下
#include<stdio.h>
int main()
{
	int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int arr2[3][4] = { 1,2,3,4,5 };
	int arr3[3][4] = { {1,2} ,{3,4}, {5,6} };
	int arr4[][2] = { 1,2,3,4,5,6,7,8,9 };
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		int j = 0;
		for (j = 0; j < 2; j++)
		{
			printf("%d ", arr4[i][j]);
		}
		printf("\n");
	}

	return 0;
}

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

我们看这个代码
#include<stdio.h>
int main()
{
	int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
		}
	}

	return 0;
}

结果为

 我们会发现,还是每个元素差四个字节

也就是说二维数组也是和一维数组一样线性连续存储的!如下图所示,前四个为第一行,中间为第二行,后面为第三行,我们可以把一个二维数组当成一个一维数组来理解

 那我们二维数组也就可以使用一维数组那样采用指针的方式来打印

#include<stdio.h>
int main()
{
	int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
	}
	printf("\n");
	int* p = &arr[0][0];
	for (i = 0; i < 12; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

 输出结果为

 而且我们二维数组每一行都可以理解为一个一维数组,这个一维数组的数组名为arr[0],arr[1]......如下图所示

 所以二维数组也是一维数组的数组

所以我们的打印数组也可以这样进行

#include<stdio.h>
int main()
{
	int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int i = 0;
	for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
	{
		int j = 0;
		for (j = 0; j < sizeof(arr[0])/sizeof(arr[0][0]); j++)
		{
			printf("%d ", arr[i][j]);
		}
	}
}

 

 三、数组越界

数组的下标是有范围限制的。
数组的下规定是从 0 开始的,如果数组有 n 个元素,最后一个元素的下标就是 n-1
所以数组的下标如果小于 0 ,或者大于 n-1 ,就是数组越界访问了,超出了数组合法空间的访问。
C 语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就 是正确的, 所以程序员写代码时,最好自己做越界的检查
比如下面的代码
#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		printf("%d\n", arr[i]);//当i等于10的时候,越界访问了
	}
	return 0;
}

运行结果为,其中就出现了一个明显的错误。

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

四、数组作为函数参数

往往我们在写代码的时候,会将数组作为参数传个函数,比如说我们想写出一个冒泡排序。

1.关于冒泡排序的经典错误标准零分

那么什么是冒泡排序呢?

比如说我们想要对下面一组数据进行升序的话

 我们是这样想的,我们让9和8进行比较,如果9大于8,那么9和8进行交换,然后交换以后变成以下

此时我们让第二个和第三个位置数据继续进行比较,并进行交换。然后重复下去,直到最后会变成

我们发现9已经来到了他应该来到的最终位置,那么我们前面八个数还需要进行排序。我们继续采用相同的方法。我们会发现,前八个数字最终也会

 此时我们发现8也来到了他应该来到的最终位置.

不妨我们就将这称作一趟冒泡排序,每一趟冒泡排序都可以使得一个数放到他应该来到的最终位置

那么问题来了,如果有10个数字,那么需要多少趟?

显然为9趟,对于任意n个数字,只需n-1趟冒泡排序。

于是我们就有冒泡排序的一个基本框架了

 这里的for循环可以确定需要多少趟冒泡排序,而里面每一层都是一趟冒泡排序

那么一趟需要比较多少次呢?

我们回顾前面的那个案例,不难发现,第一趟,需要9次比较,第二趟需要8次比较,我们总结规律,n个元素需要n-i-1次比较。

由此得到代码实现如下所示

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void sort(int arr[])
{
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (int j = 0; j < sz - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = 0;
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[] = { 4,5,1,3,7,10,100,45,323,852};
	sort(arr);
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", arr[i]);
	}
	return 0;
}

 然而当我们进行运行的时候,我们发现结果并非我们所期望的

 并没有进行交换,这是为什么呢?其实问题就出在了sizeof,我们数组的传参传的其实是一个指针,而非整个数组,所以sizeof(arr)计算出来的并不是一个数组的长度,而是arr这个指针的大小,所以我们就应该将sz给放到主函数中,然后在传参的时候传入sz

代码实现如下

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void sort(int arr[],int sz)
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (int j = 0; j < sz - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = 0;
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[] = { 4,5,1,3,7,10,100,45,323,852};
	int sz = sizeof(arr) / sizeof(arr[0]);
	sort(arr,sz);
	int i;
	for (i = 0; i < sz; i++)
	{
		printf("%d\n", arr[i]);
	}
	return 0;
}

运行后结果为

可见符合我们的预期

2.数组名到底是什么呢

我们在冒泡排序中,数组名一会是指针,一会代表整个数组,一会代表首元素地址。相信到了这块很多人都晕了,数组名到底是什么呢?

(1)一般的,数组名是首元素地址

一般情况下数组名是首元素地址,那么很多人就更加困惑了,什么是非一般情况呢?先不要着急,我们先来看看数组名是首元素地址的案例

int main()
{
	int arr[10] = { 0 };
	printf("%p\n", &arr[0]);
	printf("%p\n", arr);
	return 0;
}

运行之后

可见数组名就是首元素地址,而地址就是我们的指针。

(2)数组不是首元素地址的两个特例

第一个特例,sizeof(数组名)

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%d", sizeof(arr));
	return 0;
}

运行结果为

 

其实,sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节

第二个特例,&数组名

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%p\n", &arr[0]);
	printf("%p\n", arr);
	printf("%d\n", sizeof(arr));
	printf("%p\n", &arr);
	return 0;
}

运行结果为

 可见这里的数组名表示的是整个数组,&数组名取出的是数组的地址

(3)总结

一般情况下,数组名是首元素的地址,但有两个例外,一个是sizeof(数组名),一个是&数组名,这两种情况下,数组名代表的是整个数组。

(4)&数组名和直接使用数组名的区别

在这里,又有人有了一些困惑,arr直接打印出来的地址和&arr出来的地址有什么区别呢?看上去打印出来的值都一样啊?

 

假设上面是我们的arr数组,我们运行如下代码
#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0]+1);
	printf("%p\n", arr);
	printf("%p\n", arr+1);
	printf("%p\n", &arr);
	printf("%p\n", &arr+1);
	return 0;
}

运行后结果为

 可见,第一组+1后的地址是+4,第二组+1后的地址也是+4,而第三组+1后的地址是加了40

我们发现,虽然结果一样,也就是他们的起点一样,但是他们的含义不同,&arr+1是直接跳过整个数组,而前两者仅仅跳过一个元素。

3.再次研究冒泡排序

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void sort(int arr[],int sz)
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (int j = 0; j < sz - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = 0;
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[] = { 4,5,1,3,7,10,100,45,323,852};
	int sz = sizeof(arr) / sizeof(arr[0]);
	sort(arr,sz);
	int i;
	for (i = 0; i < sz; i++)
	{
		printf("%d\n", arr[i]);
	}
	return 0;
}

我们现在应该能够理解了这段代码中,为什么sz要放在主函数内部了,虽然我传的是一个数组名,但是此时的数组名代表着首元素地址,而我们的形参虽然人模狗样的写着一个数组,但是其实他本质上是一个指针。也就说我们可以将他改为一个指针变量。

我们可以将其改为指针试一试

void sort(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (int j = 0; j < sz - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = 0;
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

我们运行后,这个代码仍然是正确的,所以我们传数组可以使用一个指针来接受。当然我们也可以使用一个数组的形式来进行接受,这样其实更加便于初学者理解。

而我们传入一个指针以后,我们就可以对这个指针进行+1,而整型指针+1,向后移动4个字节。刚好就是下一个数据。所以采用指针可以顺藤摸瓜,拿到整个数组的值。

当然有人就有一些困惑了,为什么我们平时函数传参的时候是直接拷贝过去,而数组却是传地址呢?这一点大家可以思考以下,如果我有10000个元素的数组,那么我们还要传整个数组的话,那么对这对于计算机内存上,运行速度上都会产生极大的浪费。所以使用指针传参也是很合理的。


总结

本节主要讲解了数组的一些基本知识点,基本概念,以及数组传参时候的一些坑,如果对你有一些帮助的话,不要忘记点赞加收藏哦!!!

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

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

相关文章

SpringBoot SpringBoot 开发实用篇 2 配置高级 2.2 松散绑定

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇2 配置高级2.2 松散绑定2.2.1 问题引入2.2.2 松散绑定2.2.3 小结2 配…

MySQL学习笔记:模型2

序言 《MySQL45讲》 为什么表数据删除一半&#xff0c;表文件大小不变&#xff1f; 表数据既可以存在共享表空间里&#xff0c;也可以是单独的文件。这个行为是由参数 innodb_file_per_table 控制的&#xff1a; 这个参数设置为 OFF 表示的是&#xff0c;表的数据放在系统共…

错字修改 | 布署1个中文文文本拼蟹纠错模型

内容一览&#xff1a;中文文本错误的种类之一为拼写错误&#xff0c;本篇文章为利用 BART 预训练方法实现中文文本纠错功能的模型部署教程。 关键词&#xff1a;BART 中文拼写纠错 NLP 本文首发自微信公众号&#xff1a;HyperAI超神经 中文文本错误3大障碍&#xff1a;拼写、语…

Chapter9.1:线性系统的状态空间分析与综合(上)

此系列属于胡寿松《自动控制原理题海与考研指导》(第三版)习题精选&#xff0c;仅包含部分经典习题&#xff0c;需要完整版习题答案请自行查找&#xff0c;本系列属于知识点巩固部分&#xff0c;搭配如下几个系列进行学习&#xff0c;可用于期末考试和考研复习。 自动控制原理(…

第六节.常用Linux命令—chmod修改目录权限,组管理,用户管理

第六节.常用Linux命令—chmod修改目录权限&#xff0c;组管理&#xff0c;用户管理 1. chmod&#xff1a;可以修改用户/文件/目录的权限 1).命令格式: chmod(代表增加权限)/-(代表减少权限) r(可读权限)w(可写权限)x(可执行权限) 文件名/目录名 2.组管理&#xff1a; 1).终端…

年产5000吨饼干食品加工厂的工艺设计

目 录 摘 要 I Abstract II 第1章 绪论 1 1.1概述 1 1.2饼干的特点 1 1.2.1适宜大规模生产 1 1.2.2容易消化吸收 1 1.2.3食用方便 1 1.2.4营养价值高 2 1.3设计依据 2 1.4 设计范围 2 1.4.1 一般部分 2 1.4.2 重点部分 2 1.4.3 图纸 3 1.5设计指导思想 3 1.5.1 指导思想 3 1.5.…

org.activiti.validation.validator

org.activiti.validation.validator目录概述需求&#xff1a;设计思路实现思路分析1.ActivitiEventListenerValidator3.AssociationValidator4.validateAtLeastOneExecutable5.DataObjectValidator拓展实现参考资料和推荐阅读Survive by day and develop by night. talk for im…

【信号和槽】

前言 信号和槽是QT界面框架的一个核心特性&#xff0c;其重要性和MFC的消息映射机制一样。只要用QT开发项目&#xff0c;就一定会用到&#xff0c;所以必须100%熟练掌握&#xff0c;烂熟于心。 0x0 需要理解的概念 信号&#xff1a;特定情况下被发射的事件。鼠标单击按钮&…

基于复合粒子群优化的模糊神经预测控制的研究(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…

boot+mp搭建版本踩坑记录

最近项目搭建中遇到的一些问题,涉及到 mp 版本 swagger集成等 文章目录前言一、引入mp启动报错1 相关配置2 报错 如下3 解决方案二、引入swagger1 引入的pom2 报错如下:3 解决方案三. 项目启动自动打开swagger页面总结前言 由于使用高版本springboot 导致集成遇到的一些问题 一…

Spring Boot+Netty+Websocket实现后台向前端推送信息

Netty 是一个利用 Java 的高级网络的能力&#xff0c;隐藏其背后的复杂性而提供一个易于使用的API的客户端/服务器框架。 可能在此之前你没有接触过&#xff0c;不过不要担心&#xff0c;下面我们通过一个消息推送的例子来看一下netty是怎么使用的。 1.添加Maven依赖 <!--…

动态代理静态代理

一、使用背景 将目标类包裹起来&#xff0c;对目标类增加一个前置操作和一个后置操作&#xff0c;比如添加日志&#xff0c;在调用目标类前、调用目标后添加日志。 感觉静态代理与动态代理的核心思想&#xff0c;都是根据目标类&#xff0c;拿到目标实现的接口&#xff0c;和…

【软考】-- 操作系统(上)

目录&#xff1a;操作系统&#xff08;上&#xff09;第一节 操作系统概述&#x1f384;一、操作系统基本概念1️⃣操作系统的五大部分&#xff1a;&#x1f38b;二、操作系统的分类1️⃣批处理操作系统&#xff1a;2️⃣分时操作系统&#xff1a;3️⃣实时操作系统&#xff1a…

STC51单片机28——跑马灯

//使用P1口流水点亮8位LED #include<reg51.h> //包含单片机寄存器的头文件 /**************************************** 函数功能&#xff1a;延时一段时间 *****************************************/ void delay(void) { unsigned char i,j; for(i…

Jetpack Compose 重写TopAppBar 实现标题多行折叠

没有效果图一律当水贴处理 效果动图 前言 想用composes实现类似CSDN的文章详细页面的标题栏 上滑隐藏标题后标题栏显示标题 compose.material3下的TopAppBar不能嵌套滚动 MediumTopAppBar 便使用了MediumTopAppBar一开始用着没什么问题&#xff0c;但是标题字数多了&…

一天完成react面试准备

什么是 React的refs&#xff1f;为什么它们很重要 refs允许你直接访问DOM元素或组件实例。为了使用它们&#xff0c;可以向组件添加个ref属性。 如果该属性的值是一个回调函数&#xff0c;它将接受底层的DOM元素或组件的已挂载实例作为其第一个参数。可以在组件中存储它。 ex…

字体图标以及svg图片的使用vite和webpack

先说下字体图标的使用 首先去阿里巴巴矢量图标库&#xff0c;选择你需要的图标&#xff08;可将svg图片自己上传&#xff09;添加到项目里&#xff0c;可以生成在线链接&#xff0c;或者下载资源包到本地。 资源包形式&#xff1a;在项目里创建一个fonts文件夹&#xff0c;将下…

linux 安装rar工具

1.到官网下载对应的编译包 点击跳转 也可以直接到我上传的资源去下载 https://download.csdn.net/download/liudongyang123/87032929https://download.csdn.net/download/liudongyang123/870329292.解压 tar -xf rarlinux-x64-620b2.tar.gz 3.进入到解压后的文件夹&#xf…

Spring Cloud Alibaba 版本对照表,集成nacos,sentinel,seata

一、Spring Cloud Alibaba 版本对照网址 https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E 二、集成nacos nacos源码编译打包_qq_41369135的博客-CSDN博客 连接mysql nacos\conf下的application.properties spring.datasource.…

JDBC:PreparedStatement 插入BLOB类型的数据,PreparedStatement 批量处理,Connection 事务处理

JDBC&#xff1a;PreparedStatement 插入BLOB类型的数据&#xff0c;PreparedStatement 批量处理&#xff0c;Connection 事务处理 每博一文案 村上春树说: 你要做一个不动声色的大人了&#xff0c;不准情绪化&#xff0c;不准偷偷想念&#xff0c;不准回头看自己&#xff0c;…