带你学C语言~指针(2)

发布时间:2023年12月19日

目录

🏉前言

🚀?数组名的理解

🚀使用指针访问数组

?一维数组传参的本质

?冒泡排序

🏆二级指针

🏆指针数组

🏆指针数组模拟二维数组

🎉结束语


🏉前言

上一章,小赵和各位聊了关于C语言指针的开篇章,我们聊了指针最初的模样,但指针这个东西远远不止上一章那样简单,因为我们开辟的内存远远不止这么简单,毕竟还有数组的开辟,还有函数等等这些,包括我们的指针它也是有内存的,那我们究竟该如何去访问它们的地址,如何使用它们的地址内存,这样的地址内存又究竟对我们编写代码会带来什么样革命性的改变,下面小赵讲讲一一为你解答。

🚀?数组名的理解

首先想和各位说的一点就是,其实我们的数组名就是我们这个数组的地址,那这个数组的地址究竟是整个数组的地址呢?还是说是我们的数组首元素的地址,还是说是我们数组结尾的那个地址。这里我们打印出来看看。

?那我们通过我们对我们的数组打印地址,我们发现我们的首元素地址和我们的数组名代表的地址是一样的,那么我们的数组的地址其实也就明白了。

那如果我们对我们的地址名取地址呢?这里我们也可以打印看看。

这里我们惊讶的发现这两个地址,居然是一样的,那这样是否可以说明我们&arr与arr,是一样的?

这里小赵给大家的解释一下,虽然这两个地址是一样的但含义却是极大的不同,这个就和我们之前聊的单位一样,(char int 等),&arr取得是整个数组的地址,所以如果你对它加一,它的单位实际上就是加了一个数组,那另外一个呢?如果只是arr,它其实代表的就是首元素地址,几乎等价于&arr[0],那它加的就是以每一个数组的元素的大小为单位,那么它的加一,就是加到数组的下一个数组的元素。

🚀使用指针访问数组

好了有了上面的知识作为支撑,我们就可以试着去探讨如何用我们的指针和我们的数组相联系了。

int main()
{
	int arr[10] = { 0};
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);//数组有多少个元素
	int* p = arr;//取首元素地址
	for (int i = 0; i < sz; i++)
	{
		scanf("%d", p + i);//给数组每个位置赋值
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d", *(p + i));//打印数组每个位置
	}
	return 0;
}

这个代码小赵要和大家解释一下,这里我们的数组是定义好的,而且我们的指针指向的是首元素的地址,那么我们其实对它加加就相当于从数组首元素向后走,其实还要给大家补个概念,其实我们的指针指向那个地址,那我们的p其实就相当于这个地址,这也就是我们说的指针=地址。那这里就是地址就不用加&,而下面我们打印的是地址里面的东西所以要解地址操作。?

那这时候有人要问了,能不能超过这个数组进行访问了,其实是不行的,因为这里就是一种越界。

int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);//数组有多少个元素
	int* p = arr;//取首元素地址
	for (i = 0; i < sz+1; i++)
	{
		printf("%d", *(p + i));//打印数组每个位置
	}
	return 0;
}

?

那这个空间就是未知打印的东西也是未知的。再看这个?

int main()
{
	int arr[3] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);//数组有多少个元素
	int* p = arr;//取首元素地址

	for ( i = 0; i < sz+1; i++)
	{
		scanf("%d", p + i);//给数组每个位置赋值
	}
	for (i = 0; i < sz+1; i++)
	{
		printf("%d", *(p + i));//打印数组每个位置
	}
	return 0;
}

在这里我们就使用了未被定义的空间,那么我们的程序是会报错的。所以这种未被定义的空间是不能使用的,我们指针指向它的时候,其实我们的指针也就相当于野指针。?

?一维数组传参的本质

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

在讲一维数组传参本质前,先给大家看一段代码,这个代码应该就是我们学指针前所用的数组传入函数的方式,但我们其实不知道的是,我们传入的究竟是整个数组的,还是数组的某一个呢?这个代码刚好可以帮我门验证一下这一点。?

在这里我们可以看到我们代码运行后的结果是?10,1这就证明了,我们传入的数组其实只传了一个,那么为什么会这样呢?其实结合小赵这章所聊的内容,我们可以知道我们传入函数的是数组名,那数组名是什么呢?其实就是指针啦,那就证明我们传入函数的其实是个数组首元素指针。那这就好办了,那我们以后数组,不就可以用指针传了吗?而且指针改的是地址,可以让我们在函数中的操作,进入我们的数组中。这里补的一点是,sizeof(arr),计算的是这个所代表的存在里面东西的大小,如果在函数外面,它表示就是整个数组(这一点比较特殊,只有在sizeof这里才代表这个意思。),而我们在函数里面它代表的就是我们传入的首元素的地址,计算的也只有数组首元素的大小。)

void test(int* arr)//参数写成指针形式
{
	printf("%d\n", sizeof(arr) / sizeof(arr[0]));//计算?个指针变量的??
}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("sz1 = %d\n", sz1);
	test(arr);
	return 0;
}

这里我们用指针试试 ,发现结果一样,与我们的猜想是一致的。

?冒泡排序

有了上面的知识我们就可以试着用指针来编我们的冒泡排序了。

#include <stdio.h>
void BOBBLE(int* a)//优化版冒泡排序
{
	for (int m = 0; m < 4; m++)
	{
		int flag = 1;//判断是否排序完成
		for (int j = 0; j < 4 - m; j++)
		{
			if (a[j] >a[j + 1])//如果a[j+i]都大于arr[j],则证明排序已经完成了
			{
				flag = 0;//如果排序未完成,那么将falg置为零
				int t = a[j + 1];
				a[j + 1] = a[j];
				a[j] = t;
			}
		}
		if (flag == 1)//如果完成,结束循环
			break;

	}

}
int main()
{
	int a[5] = { 0 };
	int i;
	for (i = 0; i < 5; i++)
	{
		scanf("%d", &a[i]);
	}
	BOBBLE(a);
	for (int m = 0; m < 5; m++)
	{
		printf("%d", a[m]);
	}
	return 0;
}

我们会发现有了指针的加入,让我们原本冗杂的长的代码变得更容易看懂,可以将我们的冒泡排序加入到我们的函数中去了,而且由于我们的指针改的其实是数组地址里面的内容,那这就让我们的数组可以真正的改变了(这个有点类似(1)的交换?)。

同时我们对我们的之前的冒泡排序进行了优化,让我们的代码可以不用执行的很复杂,大大加快代码了运行速度。

🏆二级指针

那好了数组解决了吧,那我们一级指针的地址咋说,谁能来管管呢?就这样我们的二级指针诞生了。

当然,我们得先看看我们指针是否有地址,看看它的地址。

我们看到我们指针指向的地址,与我们指针本身的地址是不一样的,这也就间接证明了,我们的指针其实是有自己独立的地址。

那我们的二级指针是什么样的,其实也蛮简单的,我们可以用上一章的思想解决,打开一个空间要一个钥匙,那我们现在打开两个呢?那不就是两个吗。

int main()
{
	int a = 0;
	int* p = &a;
	int** b = &p;
	printf("%p\n", &a);
	printf("%p\n", p);
	printf("%p\n", &p);
	printf("%p", b);
}

那我怎么才能访问到里面的a能其实也就是开两次锁。

?

🏆指针数组

那紧接着下来就是,我们有木有这样一个数组里面存的是指针呢?那其实也是有的。我们之前聊到,数组前面的那个就是它里面的元素类型,那我们只要将前面写成int*,char*等就可以了。

int main()
{
	int a = 0;
	int* p = &a;
	int* b[1] = { p };//指针数组
	printf("%d", *(b[0]));
}

🏆指针数组模拟二维数组

那一维数组聊完了,那二维数组呢?这里就不得不提小赵前面的概念了,二维数组里面每个元素其实就是一维数组,那既然是一维数组,我们是不是可以试着用一维数组的首元素地址代替呢?然后再对这个元素地址进行解指针操作,加一加二等,这样是不是就让指针数组模拟二维数组的操作完成了。?

#include <stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int* parr[3] = { arr1, arr2, arr3 };//数组名是数组?元素的地址,类型是int*的,就可以存放在parr数组中
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);//这里要解释一下,其实我们a[1]=*(a+1)的操作,那我们这里的arr[1][2]其实也就等于*(arr[1]+2)的操作。
		}
		printf("\n");
	}
	
	return 0;
}

这里要和家人们 解释的其实就是我批注的那一块,我们的arr[]这个操作其实就是在对我们的arr进行解指针的操作,而括号里面的数就是我们arr+数字,地址的移动,然后进行解指针。?

🎉结束语

好了小赵今天的分享就到这里了,如果大家有什么不明白的地方可以在小赵的下方留言哦,同时如果小赵有什么地方说得不对也希望得到大家的指点,谢谢各位家人们的支持。你们的支持是小赵创作的动力,加油。

如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持小赵,如有不足还请指点,小赵及时改正,感谢大家支持!!!

文章来源:https://blog.csdn.net/zcg3063126760/article/details/135073693
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。