深入理解指针(三)--字符指针、数组指针、函数指针、函数指针数组

字符指针变量

字符指针--char* 是指针的类型。

char ch = 'w';

char* pch = &ch;

以上是一般的使用,字符的地址取出,放在字符指针变量中。


再看下一种使用方式

const char* p = "abcdef";

以上的字符串是常量字符串,并且它是常量,不能被修改。这里大家可能会认为是把字符串“abcdef”存放在字符指针中,其实并不是这样,而是把首元素地址放在指针中

这里的常量字符串和数组用法一样,并且存储的地址也是连续的。


我们来看一下下面有趣的题目: 

#include <stdio.h>
int main()
{
	char str1[] = "abcdef"; //而数组则是数组名为地址,它会开辟新的地址
	char str2[] = "abcdef";

	const char* str3 = "abcdef"; //指针指向的时常量字符串的首个字符地址
	const char* str4 = "abcdef";

	if (str1 == str2)
	{
		printf("str1 and str2 are same\n");
	}
	else
	{
		printf("str1 and str2 are not same\n");

	}
	if (str3 == str4)
	{
		printf("str3 and str4 are same\n");

	}
	else
	{
		printf("str3 and str4 are not same\n");

	}
	return 0;
}

 这⾥str3和str4指向的是⼀个同⼀个常量字符串。当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始 化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。

 总结:

数组是会创建新的地址保存数据

而指针是指向同一个内存的


数组指针变量

首先,我们来类比一下:

整型指针变量:int* p存放的是整型变量的地址,是指向整数数据的指针。

字符指针变量:char* p存放的是字符变量的地址,是指向字符数据的指针。

那数组指针比变量应该就是:存放数组的地址,是指向数组数据的指针。

int arr[5];

int (*p)[5]; //这个就是数组指针变量


注意

在之前我们学习过一个指针数组,它是一个数组,而不是一个变量。

int* p[5]; //这是指针数组

数组中存储了5个指针变量 int*。 首先[]的优先级比*高,所以p先和[]结合,形成了数组,然后再和int *结合,所以形成了指针数组。

而数组指针,实际上是一个指针变量。

int (*p)[5];

先括号把*p结合起来,形成指针,再和[]结合,形成数组指针变量。


二维数组传参的本质

以代码的形式给大家展示:

#include <stdio.h>
void Print(int(*arr)[5], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			printf("%d ", *(*(arr + i) + j));
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
	Print(arr, 3, 5);

	return 0;
}

 问题与解答(递进式理解代码)

1.问题:二维数组传参为什么用数组指针接收?

解答:⾸先我们再次理解⼀下⼆维数组,⼆维数组其实可以看做是每个元素是⼀维数组的数组,也就是⼆维 数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。


2 .问题:为什么打印二维数组的元素时是*(*(arr + i) + j)这种写法?

解答:大家知道一维数组的元素的取法-->*(arr+i)这样遍历所有元素,第一个问题我们得知二维数组的首元素是一个一维数组,所以*(arr + i)只是取出了每一行的一维数组,想要遍历里面元素,需要在加一个变量做遍历下标---> *(*(arr + i) + j).


大家如果不了解数组内容,参考:你真的懂数组吗?从0到1的深层理解 


 函数指针变量

有了前面数组指针变量的铺垫,相信大家对函数指针变量心里也有了猜测。 

其实,函数指针变量就是用来存放函数的地址的,能够通过函数的地址来调用函数。


 来思考一下函数是否有地址?

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}
//&函数名 和 函数名 拿到的都是函数的地址
int main()
{
	printf("%p\n", &Add);
	printf("%p\n", Add);
	return 0;
}

 其实函数是有地址的,函数名就是函数的地址,当然也可以通过 &函数名 的⽅ 式获得函数的地址,俩个方法都可以取到地址。


那大家可能就会问了,那函数地址存储在哪里呢?

函数地址其实是存储在函数指针变量中。 

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}

int main()
{

    int(*p)(int, int) = &Add;
    int(*p)(int x, int y) = &Add; //x和y写上或者省略都是可以的
    return 0;
}

 其实函数指针的创建和数组指针变量的创建时差不多的。


 一段有趣的代码

( *(void (*)( ) ) 0)( );

 大家了解一下即可。


typedef 关键字

typedef 是⽤来类型重命名的,可以将复杂的类型,简单化。

⽐如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤:

 typedef unsigned int uint;

//将unsigned int 重新命名为 unit

如果是指针类型,能否重命名呢?其实也是可以的,⽐如,将 int* 重命名为 ptr_t ,这样写:

 typedef int* ptr_t;

但是对于数组指针和函数指针稍微有点区别:

⽐如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:

 typedef int(*parr_t)[5]; //新的类型要在*的右边

函数指针类型的重命名也是⼀样的,⽐如,将 void(*)(int) 类型重命名为 pf_t ,就可以这样写:

 typedef void(*pfun_t)(int); //新的类型名必须在*的右边


 函数指针数组

数组是⼀个存放相同类型数据的存储空间,我们已经学习了指针数组,

int * arr[10]; 

//数组的每个元素是int*

那要把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢? 

int (*parr1[3])();

parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?

是 int (*)() 类型的函数指针。


 转移表

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

#include <stdio.h>
int add(int x, int y)
{
	return x + y;
}

int sub(int x, int y)
{
	return x - y;
}

int mul(int x, int y)
{
	return x * y;
}

int div(int x, int y)
{
	return x / y;
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;

	int(*p[5])(int, int) = { 0, add, sub, mul, div };
	do
	{
		printf("*****************************\n");
		printf("**1.add   2.sub**************\n");
		printf("**3.mul   4.div**************\n");
		printf("******0.exit*****************\n");
		printf("*****************************\n");
		printf("请选择:>");
		scanf("%d", &input);
		if (input > 0 && input <= 4)
		{
			printf("输入操作数:>");
			scanf("%d%d", &x, &y);
			ret = p[input](x, y);
			printf("%d\n", ret);

		}
		else
		{
			printf("输入有误\n");
		}

	} while (input);


	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值