脆皮之“指针和数组的关系”【冒泡排序】

hello,大家好呀,窝是脆皮炸鸡。这一期是关于数组和指针的,我觉得并不是很难,但是我觉着下一期可能稍难,大家有什么不懂的可以在评论区询问,加油,开始学习叭!!!

Ps:点赞收藏加关注,追番永远不迷路。

在这里插入图片描述

1. 数组名的理解

大家是否记得在上一期的指针内容里,我曾说过:数组名是数组首元素的地址。但是有两个例外,这两个例外是:sizeof(数组名),&数组名。除了这两个情况,其余情况下,数组名就是数组首元素的地址。

----------------------------------------------- 1 -----------------------------------------------

举个例子:sizeof(数组名)

	int arr[7] = { 0 };
	printf("sizeof(arr)=%d\n", sizeof(arr)); //这里sizeof是在计算arr的大小

大家知道此处打印的结果是什么吗,是28。如果按之前说的”数组名都是数组首元素的地址“,那首元素的地址占四个字节,为什么输出是28呢?

因为,此处数组名代表的并不是数组首元素地址。sizeof(数组名),其中的数组名表示的是整个数组,计算的是整个数组的大小,单位是字节。我们再回头看,这个数组共7个整数,一个整数占4个字节,所以一共占4*7=28个字节

----------------------------------------------- 2 -----------------------------------------------

举个例子:&数组名

int arr1[7] = { 0 };
printf("arr1    =%d\n", arr1);  //这里的数组名在两种情况外,是数组首元素地址
printf("&arr1   =%d\n", &arr1);  //整个数组的地址

事实上,打印出来的两个结果是相同的,都是数组首元素地址。

这是合乎情理的,arr1(不是那两种特殊情况)代表的是数组首元素地址;&arr1代表的是数组的地址。从下图可以看出,两者的起始地址一样。

在这里插入图片描述

在此处我们看不出它俩的差异。

大家是否记得之前的一句话“指针类型决定了指针的差异”。整型指针+1,跳过4个字节;字符指针+1,跳过1个字节。那数组指针+1,应当是跳过整个数组的。

printf("arr1    =%d\n", arr1);  //数组名
printf("arr1+1  =%d\n", arr1+1);  

printf("&arr1   =%d\n", &arr1);  //整个数组的地址
printf("&arr1+1 =%d\n", &arr1+1);  

在这里插入图片描述

(最后两个:44到48,+4;44到72,+28)图上显示:数组首元素地址+1,跳过4个字节;数组地址+1,跳过28个字节,即跳过整个数组。

总结:&数组名:这里的数组名表示整个数组,取出的是整个数组的地址

2. 使用指针访问数组

在此强调一下:

  • 数组是数组(是一块连续的空间,可存放一个或多个数组)
  • 指针是指针(用于存放地址的变量)
  • 它俩不是一回事,但是可以使用指针来访问地址。

为什么可以用指针来访问数组?
1.数组在内存中是连续存放的(指针+1是很方便遍历数组的)
2.指针的元素很方便遍历数组,取出数组内容。

我注释掉的方法是以前学习的方法,没有注释的方法是使用指针来访问的

int main()
{
	int arr[6] = { 0 };
	//输入
	int index = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (index = 0; index < sz; index++)
	{
		/*scanf("%d", &arr[index]); */
		scanf("%d", arr + index); 
		//arr是首元素地址,+1,跳过(4个字节)一个整数。
		//arr+index,第一次循环就是arr+0,即首元素地址;第二次循环,arr+1,即下一个整数
	}

	//输出
	for (index = 0; index < sz; index++)
	{
		/*printf("%d ", arr[index]); */
		printf("%d ", *(arr + index));
		//将每个元素的地址解引用,即可找到地址所指向的对象
	}
	return 0;
}

将输出的两种方式对比,arr[index] 和 * (arr+index) 它俩是完全等价滴。【其实,编译器在看到arr[index]是转化成 *(arr+index)来看的】

3. 一维数组传参的本质

数组是可以通过实参传递给函数的,现在我们来讨论一下数组传参的本质。大家先思考一下,假设我们要将数组int arr[7]传过去,当时我们说过,数组传的是数组名(实参),形参用什么来接收嘞?int arr [ ](数组大小可忽略)

在这里插入图片描述
第一种我们直接计算,结果是正确的;但在函数里却错了,为什么呢?

我们仔细看一下,print(arr),不是那两种例外情况,所以arr是数组首元素地址,要是地址的话,我们应该用int* arr接收的,然后(首元素地址4个字节) / (首元素地址4个字节)=1。到这我们就找到问题了。【之前我们之所以用int arr[ ]接收,只是为了方便理解,当时还没有学指针。】数组传参的时候,形参可以写成数组(易理解,但其实本质还是指针),也可以写指针变量。

总结:从中可以看出,数组传参的本质,传递的是首元素的地址,即便我们将形参写成数组的形式,它本质上也是一个指针变量。

那如果我们就是想将数组传过去呢?这时,我们不仅需要将数组名传过去,还需要将数组元素个数sz传过去,接下来举例:先将数组传给函数,再通过函数打印数组。

void print(int* arr, int sz)
{
	int index = 0;
	for (index = 0; index < sz; index++)
	{
		printf("%d ", *(arr + index));
	}
}
int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(arr, sz);  将数组大小也传过去
	return 0;
}

4. 冒泡排序

冒泡排序的核心思想就是:两两相邻的元素进行比较。不满足顺序就交换,满足顺序就比较下一对。

以 “将乱序的数字变为有序的数字(升序/降序) ” 为例进行分析。(我这里是升序)

  1. 首先输入数列元素。
  2. 然后冒泡排序,确定冒泡排序需要几趟(index)
  3. 在每一趟里,又要进行几次比较。把什么进行比较呢?将【第一个(下标为0的元素)】和【第二个元素(下标为1的元素)】比较,第二个和第三个比较…(j),总是将arr[j]和arr[j+1]进行比较
  4. 最后打印出来。

先展示第一趟冒泡排序:看完图片之后发现,一趟冒泡排序只将一个数字放在它本应该在的位置。

在这里插入图片描述

第二趟冒号排序:
在这里插入图片描述

之后的和这类似,现在思考一下:

  1. 我有10个元素,需要几趟冒号排序呢?9趟(你想想,9个数字都已经在正确的位置了,最后一个元素的位置肯定正确呀)所以,sz个元素,sz-1趟
  2. 10个元素,第一趟进行9次比较,第二趟进行8次比较,所以:sz个元素,第一次进行sz-1次比较,第二次进行sz-2次比较…那么在循环里,如何描述几次比较呢?sz-1写成sz-1-index(第一趟index是0,这里就是sz-1),sz-2就是sz-1-1(因为此时index是1,所以是sz-1-1)
void input(int* arr, int sz);  //用于输入数字
void bubble(int* arr, int sz);  //用于冒泡排序
void print(int* arr, int sz);  //用于打印排序好的数组

int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//输入数字
	input(arr, sz);
	//将乱序的数字排序
	bubble(arr, sz);
	//最后将升序的数字打印出来
	print(arr, sz);
}
void input(int* arr, int sz)  //用于输入数字
{
	int index = 0;
	for (index = 0; index < sz; index++)
	{
		scanf("%d", arr + index);
	}
}
void bubble(int* arr, int sz)  //用于冒泡排序
{
	int i = 0;
	for (i = 0; i < sz - 1; i++) //这是确定几趟
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)//一趟里面比较几次
		{
			if (arr[j] > arr[j + 1]) //判断:如果前一个数字>后一个,交换
			{
				int tmp = 0;
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
void print(int* arr, int sz)  //用于打印排序好的数组
{
	int q = 0;
	for (q = 0; q < sz; q++)
	{
		printf("%d ", *(arr + q));
	}
}

5. 二级指针

我们之前学习的都是一级指针,比如:int*,char*,float*
一级指针是用来存放(普通变量)的地址
二级指针是用来存放(一级指针变量)的地址
int* ,int**

	int a = 90;
	int* pa = &a;  //pa是一级指针变量

这里,我将a的地址放在指针变量pa里,但是大家想想,指针变量也是变量,它也有自己的地址,我们又可以将指针变量的地址存储起来放在ppa。并且我们可以通过ppa找到最开始的普通变量a:printf(“%d”,**ppa); 也可以通过ppa改变a的值。

int*      * ppa = &pa;  //ppa是二级指针变量
        //这个*说明ppa是指针变量
//int*说明ppa指向的pa的类型是int*

之前我们可以通过一级指针访问数组元素,但是在这里说明,二级指针和二维数组没有任何关系。

6. 指针数组

大家可以将指针当作定语,数组是主语。所以指针数组本质上是数组,数组中存放的是地址(指针)

字符数组:char arr[10]; 存放字符的数组
整型数组:int arr[10]; 存放整型的数组
指针数组:它是一个存放指针的数组,它的每个元素都是指针类型(**int * 或者char * 等等)。
char
arr[10] :存放字符指针的数组。int
arr[10]:存放整型指针的数组。

它的用处:(1)可以将相同类型的变量的地址存储在数组中,不用再一个一个的存储
(2)存储之后又可以遍历数组(元素是地址)再解引用打印最初的变量

	int a = 90;
	int b = 41;
	int c = 50;
	int* arr[3] = { &a,&b,&c };
	int index = 0;
	for (index = 0; index < 3; index++)
	{
		printf("%d ", *(arr[index]));
	}

7. 指针数组模拟二维数组

我先创建3个一维数组,再用过int*将它们关联起来(可以达到用1个一维数组维护3个一维数组的效果)

这是模拟出来的,不是真的二维数组

int arr1[4] = { 1,2,3,4 };
int arr2[4] = { 1,1,2,4, };
int arr3[4] = { 2,4,6,8 };
int* ARR[3] = { arr1,arr2,arr3 };
//这里的arr1并不是那两种例外,所以是数组首元素的地址
//我们想通过首元素地址然后遍历这个数组的所有元素
int i = 0;
for (i = 0; i < 3; i++)
{
	int j = 0;
	for (j = 0; j < 4; j++)
	{
		printf("%d ", ARR[i][j]);
		//ARR[i]等价于*(ARR+i) ,ARR[i][j]等价于*(*(ARR+I)+j)
		}
	}
	printf("\n");
}

在这里插入图片描述

ARR [i] 是访问ARR数组的元素,ARR [i] 找到的数组元素指向了整型一维数组,ARR[i][j]就是整型一维数组中的元素。

  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值