C语言————深入理解指针(2)(进阶使用)

深入理解指针——对指针的进阶使用

目录

1.数组名的理解

2.使用指针访问数组

3.一维数组传参的本质

​4.冒泡排序

5.二级指针

6.指针数组

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

8.数组指针变量

1.理解数组指针变量

2.区分数组指针变量和指针数组变量

3.数组指针变量初始化

4.数组指针类型解析

9.字符指针变量

10.二维数组传参的本质

11.函数指针变量以及函数指针数组

1.创建函数指针变量

2.使用函数指针变量

3.typedef关键字

4.函数指针数组

函数指针数组的用途:转移表


1.数组名的理解

数组名就是地址,而且还是数组首元素的地址。

我们测试下面这个程序

#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];//使用&arr[0]拿到数组首元素地址,并放在指针变量p中。
	printf("&arr[0]=%p\n", &arr[0]);
	printf("   p   =%p\n", p);
	printf("  arr  =%p\n", arr); //数组名
	return 0;
} 

我们可以看出数组名和数组首元素的地址打印出的结果是一模一样的,所以,数组名就是数组首元素的地址。

但是也有特殊的情况:

  1. sizeof(数组名),sizeo中单独放数组名的时候,这里的数组名就表示的是这整个数组,计算的就是整个数组的大小,单位是字节。
  2. &数组名,这里数组名也是表示的整个数组,取出的就是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的,类型不一样,地址+1跳过的字节就会不一样,在后面会有介绍)

除此之外,其余任何地方使用数组名,数组都表示首元素的地址。

接下来,我们测试这段代码

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("sizeof(arr) = %d \n", sizeof(arr));
	printf("&arr        = %p \n", &arr);
	printf("&arr+1      = %p \n", &arr + 1);
	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);
	return 0;
}

这是它输出的结果,我们怎么去理解这么一堆的数据呢?

我们可以通过自动窗口来查看一些元素的类型,对比一下类型

可以看出整个数组的地址和数组首元素的地址类型是有区别的,&arr是数组的整个地址,类型是int[10]*,这是数组指针变量,我们具体在后面介绍,不同类型+1是跳过一个这个类型占的字节大小空间,所以上面那输出的一堆数据就很好去理解了。

2.使用指针访问数组

根据前面的学习,我们得知:

  1. 数组在内存中是连续存放的。
  2. 指针+-整数运算,方便我们获得每一个元素的地址。

所以,可以很方便的使用指针访问数组了。

int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]); //求数组元素个数
	int* p = arr;
	//输入
	for (i = 0; i < sz; i++)
	{
		scanf("%d", p + i);     //这个等价于scanf("%d", p[i]);
		//也可也这样写
		//scanf("%d", arr + i)   //这个等价于scanf("%d",arr[i]);
	}
	//输出
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i)); //这个可以写成printf("%d",p[i]); //本质上p[i]==*(p+i)
		                             // []是下标引用操作符 
		                             //arr[i]==i[arr] <--->*(arr+i)==*(i+arr)   
	                                 //arr[i]<==>*(arr+i)<-->*(i+arr)<==>i[arr]
	}                               

}

运行上述程序,结果上图。

其实数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后再解引用来访问它。(arr[i]<==>*(arr+i))

3.一维数组传参的本质

数组可以传递给函数的,在此之前我们了解了有:

  1. 数组就是数组,是一块连续的空间(数组大小和数组元素个数、类型都有关系)
  2. 指针(变量)就是指针(变量),是一个变量,占4/8个字节(32位/64位)
  3. 数组名是地址,是首元素地址(除了两个特殊例外)
  4. 可以使用指针来访问数组

我们测试下列这一个代码,让一个数组传给一个函数,让它计算这个数组的元素个数:

//测试一维数组传参(测试把一个数组传给一个函数后,让这个函数求数组的元素个数)
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;
}

输出结果可以看出,明显有问题了,这个问题在哪呢?这就要考虑到一维数组传参的本质了:数组传参本质上传递的是数组首元素的地址(所以形参访问的数组和实参的数组是同一个数组)。由于数组名是数组首元素的地址,且在数组传参时,传递的是数组名。

void test(int arr[]) //(int arr[])<==>(int *arr) 参数写成数组形式,本质上还是指针
//所以函数形参的部分理论上应该使用指针变量接收数组首元素地址。

{
    
}

所以sizeof(数组名)计算的是一个地址的大小,而不是数组的大小,并且函数参数部分实际上是指针类型;sizeof()函数功能返回一个对象或者类型所占的内存字节数;因为指针就相当于地址,所占内存与指针指向对象没有任何关系(所有的指针变量所占内存大小相等,32位下4个字节、64位下8个字节)。

注:形参的数组是不会单独创建数组空间的,所以形参的数组是可以省略掉数组大小的。

一维数组传参,形参的部分可以携程数组的形式,也可也写成指针的形式。

void Print(int arr[10]);
//可以写成
void Print(int arr[]);
//也可也写成
void Print(int* arr);

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值