C进阶:指针(1)

IT知识
219
0
0
2024-04-30

一.指针数组

指针数组是存放指针的数组,首先得是一个数组,其次数组里的元素是指针。

所以数组名先和方括号 [ ] 结合 ,然后才是指针;

具体如下图:

实例:

代码语言:javascript

复制

int main()
{
	int a[5] = { 1,2,3,4,5 };
	int b[5] = { 5,6,7,8,9 };
	int* parr[2] = { a,b };
	int i = 0, j = 0;
	for (i = 0; i < 2; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", *(parr[i] + j)); //parr[i] 就是数组parr 中下标为i的元素
		}                                  //该元素为数组名,数组名是数组首元素的地址
		printf("\n");                      //加上 j 就是数组中下标为 j 的元素的地址
	}                                      //对其解引用就得到了该元素
	return 0;
}

当然上面代码中的 printf 语句还可以写成 printf("%d ",arr[i][j]);

运行结果:

二.数组指针

是指针,该指针指向的是一个数组,其中存放的是数组的地址。

也就是说它首先得是 一个指针,然后再说明它所指向的是一个数组

具体如下图:

操作符优先级详解见:http://t.csdn.cn/mnrSk

当然数组指针一般不用于一维数组中,通常应用在二维数组及更高维的数组中

例:

代码语言:javascript

复制

void print(int(*parr)[5], int row,int col)
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			printf("%d ", *(*(parr+i) + j));  //*(parr+i)表示数组 arr 第i行的地址
		}                                     //加上j表示第i行的第j个元素的地址
		printf("\n");                         //对其解引用就找到了这个元素
	}
}
int main()
{
	int arr[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
	print(arr, 2,5);
	return 0;
}

运行结果:

三. 数组名 vs & 数组名 (arr vs &arr)

1.数组名和&数组名的值是一样的;

2.数组名和&数组名的意义不同。

请看代码:

代码语言:javascript

复制

int main()
{
    int arr[5]={1,2,3,4,5};
    printf("arr=%p\n",arr);
    printf("&arr=%p\n\n",&arr);
    printf("arr+1=%p\n",arr+1);
    printf("&arr+!=%p\n",&arr+1);
    return 0;
}

打印结果:

这是为什么呢?

1.因为&arr 取出的是整个数组的地址,而数组的地址也是从首元素的地址开始的,数组名表示数组首元素的地址,所以打印时 arr 和 &arr 的值相同;

2.arr+1所加的其实是1个步长,步长是由数组类型决定的,arr 的类型是 int ,+1表示跳过4个字节,所以 arr 和 arr+1 相差四个字节;

3.&arr+1所加的是整个数组的长度,上方数组 arr 的长度是 20 个字节,所以&arr 和 &arr+1 相差20个字节(地址是以16进制的形式打印的,所以会呈现出这个效果)。

补充:

数组名是首元素的地址,但是有两个例外:

1.遇到sizeof时,计算的是整个数组的大小,单位是字节;

2.&数组名:数组名表示整个数组,取出的是整个数组的地址;

二维数组的数组名:

1.表示的是二维数组首元素的地址;

2.二维数组首元素的地址是第0行的地址。

四.数组指针与指针数组的辨析

例1:

例2:

五.函数指针

是指针,指向的是一个函数

所以说首先得是指针,然后才指向的是函数

具体如下图:

使用方法:

我们来看下面一段代码:

代码语言:javascript

复制

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	printf("Add=%p\n", Add);
	printf("&Add=%p\n", &Add);
	return 0;
}

打印结果:

那么这就说明上方的 int (*pf)(int,int)=&Add; 可以写成 ---> int (*pf)(int,int)=Add;

其实这个 * 星号在这里也没什么用,他是为了帮助我们更好的理解,所以上方使用函数指针时还可以写成:int ret=pf(3,5) ; 不管是哪一种写法,最后的结果都是一样的

所以函数名就是函数的地址,即函数名=&函数名

六.两段有趣的代码

1. (* (void ( * )( )0 )();

解析:

2. void ( *singal ( int , void( * ) ( int ) ) ) ( int )

解析:

这样写未免太麻烦,我们可以利用关键字 typedef 来重定义 singal 的返回值类型

如图 :

七.函数指针数组

顾名思义,函数指针数组就是存储函数指针的数组,函数指针指向的函数必须是同一类型的,即函数的参数和返回值相同。

首先得是个数组,然后再存储函数指针。

如图:

函数指针的用途:转移表(出自《C和指针》这本书);

例如我们写一个简易的计算器代码:

代码语言:javascript

复制

void menu()
{
	printf("********************************************************\n");
	printf("*********            1.Add     2.Sub          **********\n");
	printf("*********            3.Mul     4.Div          **********\n");
	printf("*********                 0.exit              **********\n");
	printf("********************************************************\n");

}
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 (*parr[5])(int, int) = { NULL,Add,Sub,Mul,Div };//使下标所对应的函数与我们期望的一样
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		int x = 0, y = 0, ret = 0;
		if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = (parr[input])(x, y);
			printf("ret=%d\n", ret);
		}
		else if (input == 0)
		{
			printf("exit\n");
		}
		else
		{
			printf("选择错误,重新选择\n");
		}
	} while (input);

	return 0;
}

如果我们不使用函数指针数组,想要实现建议计算器的程序,就要用到很多重复的代码,如果想要给计算器添加更多的计算功能,那势必又要写上许多重复的代码,是我们的程序变得杂乱,可读性不高。

😼😸本篇文章就到这里啦,如有错误或是建议,欢迎小伙伴们指出。😽😻

🦖🐋谢谢你的阅读🦄🐰