第十二章 指针(2)

1. 数组名的理解

2. 使⽤指针访问数组

3. ⼀维数组传参的本质

4. 冒泡排序

5. ⼆级指针

6. 指针数组

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

一、数组名的理解

int arr[10]={1,2,3,4,5,6,7,8,9,10}
我们平常想拿到数组的首地址,一般是通过
int *pr = &arr[0];
但是再c语言中数组名就是首元素的地址
即:&arr[0] == arr
如果不信,我们来用代码来显示一下

int main()
{
	int arr[] = { 1,2,3,4,5 };
	int* pr1 = &arr[0];
	int* pr2 = arr;
	printf("pr1的地址%p \n", pr1);
	printf("pr2的地址%p \n", pr2);
	return 0;
}

两者的代码一样,说明数组名就是数组元素的地址

在这里插入图片描述

这里有两种特殊情况
1.sizeof(数组名) : 表示的是sizeof中单独放数组名,这里的数组名表示的整个数组,计算的是整个数组的大小,单位是字节
2.&数组名:这里的&数组名,取出的是整个数组的地址(整个地址的数组和数组首元素的地址有区别的)

int main()
{
	int arr[] = { 1,2,3,4,5 };
	int* pr1 = &arr[0];
	int* pr2 = arr;
	int* pr3 = &arr;
	printf("pr1的地址%p \n", pr1);
	printf("pr2的地址%p \n", pr2);
	printf("pr3的地址%p \n", pr3);

	return 0;
}

在这里插入图片描述
这里我们可以发现,三个地址都是一样的那区别在哪里呢?


int main()
{
	int arr[] = { 1,2,3,4,5 };
	int* pr1 = &arr[0];
	int* pr2 = arr;
	int* pr3 = &arr;

	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;
}

输出的结果:
在这里插入图片描述
这里我们发现,&arr[0]和arr +1 都是跳过四个字节,而arr+1则是跳过20字节(也就是跳过了整个数组)。

总结:当&取出的是整个数组的时候,+1跳过的是整个数组,其它情况下跳过的都是一个元素(字节根据其数组的类型来确定)
大家要记好数组名的2个例外

二、使用指针访问数组

有了前面的基础做铺垫,接下来我们就可以来使用指针访问数组
这里我给大家写三种方法

#include<stdio.h>
// 111111
int main1()
{
	char arr[] = "abcdefg";  //字符串后会隐藏一个\0我们清楚
	char* pr = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
	{
		printf("%c ", *(pr + i));  //这里我们运用了指针的方式进行了打印数组
	}
	printf("\n");
	//222222
	for (int i = 0; i < sz; i++)
	{
		printf("%c ", arr[i]);  //这里我们运用了用数组下标的方式来拿到数组
	}
	return 0;
}
33333333
int main()
{
	char arr[] = "abcdefg";  //字符串后会隐藏一个\0我们清楚
	char* pr = arr;
	//这里我们可以想一个另一种方法
	//思路:我们用接收的pr通过*pr(解引用的方式)来访问该字符数组的元素,直到遇见\0停止
	while (*pr != '\0')
	{
		printf("%c ", *(pr++));  //这里是先pr与* 解引用之后再++找到下一个字符的地址再进判断;
	}
	return 0;
}

这里我们发现不管是用指针访问数组还是使用数组的下标来访问数组都是可以实现的。
那么两者有什么不同吗?
其实没有什么不同,在本质上*"(pr+i) == arr[i]",在其本质上通过数组元素来访问的时候也是要转换成⾸元素的地址+偏移转换成⾸元素的地址+偏移

三、一维数组传参的本质

数值也是可以传递给函数的,那么接下来我们来讨论一下数组传参的本质。

接下来我们来引入一个问题,我们之前都是在函数外部来计算元素个数的,那么我们可以把数组传递给函数,观察在函数内部是否可以计算数组的元素个数呢?

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

这里我们发现sizeof(数组名),计算的是整个数组的大小单位是字节,这里的数组是int且是整形所以就是40


//void  my_sizeof(int arr1[10])  第一种方法:数组用数组接收
//{
//
//	printf("%zd", sizeof(arr1));
//}
void  my_sizeof(int *pr)//指针变量,第二种方法用指针来接收
{    
//我们函数这里接收的数组首元素的地址,计算的是一个数的地址字节,而不是整个数组的字节,所以在函数内部是无法求数组的元素
	printf("%zd", sizeof(pr));
}
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	 my_sizeof(arr);
	return 0;
}

这里再不同的的配置环境下,所输出的结果是不同的,
1.在x86环境下字节为4;
2.在x64环境下为8;
我们既然可以用指针来接收数组的名字,这就说明了数组名是数组首元素的地址,其本质数组的传参传递的就是首元素的地址。

四、冒泡排序

冒泡排序:核心思想就是相邻元素进行比较

void bubble(int arr[10], int ret)
{
	int i = 0;
	for (i = 0; i < ret-1; i++)  //第一次循环确定要排几次序,10个数最多排9次序
	{
		int j = 0;
		for (j = 0; j < ret - i - 1; j++) // 第二次循环确定要交换几次,第一次交换交换9次,第二次8次.....
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j + 1];
				arr[j + 1] = arr[j];  //交换位置
				arr[j] = temp;

			}
		}
	}
}

int main()
{
	int arr[10] = { 0 };
	int ret = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < ret; i++)
	{
		scanf("%d", &arr[i]);
	}
	bubble(arr, ret);

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

上边的代码就算遇见顺序的数据也会正常执行浪费时间,我们来改造一下,当我们遇见一个顺序的时候,如果在进行这样的判断不就会浪费时间了

void bubble(int arr[10], int ret)
{
	int i = 0;
	for (i = 0; i < ret-1; i++)  //第一次循环确定要排几次序,10个数最多排9次序
	{
		int flag = 1;//假设其有序
	   int j = 0;
		for (j = 0; j <ret - i - 1; j++) // 第二次循环确定要交换几次,第一次交换交换9次,第二次8次.....
	   {
			if (arr[j] > arr[j + 1])
			{
				flag = 0;  //只要后一个比前一个大就确定其无序了
				int temp = arr[j+1];
				arr[j + 1] = arr[j];  //交换位置
				arr[j] = temp;

			}
			
       }

		if (flag == 1)
		{
			break;  //如果这一趟没有交换,则说明有序就直接跳出即可
		}

	}
}

int main()
{
	int arr[10] = { 0 };
	int ret = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < ret; i++)
	{
		scanf("%d", &arr[i]);
	}
	bubble(arr,ret);

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

这里就是运用了假设的方法。

五、二级指针

指针变量也是变量,是变量就有地址,那么指针变量的地址存放在那么呢?
答案是二级指针
在这里插入图片描述

对于二级指针来说:

(一次解引用)*prr 得到是pr, (二次解引用)**prr得到的是a,

六、指针数组

对于指针数组来说,到底是指针还是数值呢?
我们来类比一下,整形数组是储存整形的数组
字符数组是储存字符的数组,那么就可以知道指针数组就是储存指针的数组

在这里插入图片描述
指针数组的每个元素是地址(因为储存的是数组名),又可以指向一块区域

七、指针数组模拟二维数组
就是运用了上边的知识

int main()
{
	int arr1[5] = { 0,1,2,3,4 };
	int arr2[5] = { 1,2,3,4,5 };
	int arr3[5] = { 2,3,4,5,6 };
	//数组名就是数组首元素的地址,都是int*的类型可以储存到,指针数组里边去
	int* pr[3] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5;j++)
		{
			printf("%d ", pr[i][j]); //pr[i]拿到的是每个数组的元素,也就是拿到了每个上边的每一个整形数组
		}                            //prr[i][j],通过拿到每一个整形数组我们就可以,就可以访问数组中的每一个元素
		printf("\n");
	 }
	return 0;
}

在这里插入图片描述
上述模拟出来的二维数组的并非是真的二维数组,它是不连续的真正的二维数组是连续的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值