指针进阶(上)

二级指针

       二级指针是用来存放一级指针地址。
指针进阶(上)_第1张图片

指针进阶(上)_第2张图片

如何使用和解引用呢?

#include 

int main()
{
	int a = 5;
	int* p = &a;
	int** p2 = &p;
	**p2 = 10;
	printf("%d\n", a);
	return 0;
}

这里的解引用使用两颗星号的原因是:一个星号找到的是一级指针,再使用一个星号就会找到二级指针。

下面是二级指针的拆分说明:

指针进阶(上)_第3张图片

字符指针

       字符指针是存放字符的地址,如果存放字符串的地址就是首元素的地址。字符指针的类型是char*

指针进阶(上)_第4张图片

指针数组

       指针数组是数组还是指针,我们用语文的角度来分析,好孩子这里的主语是孩子,那么指针数组的主语就是数组,顾名思义就是存放指针的数组
这是指针数组的模样:

int* p[10]

如何解读这个代码:
首先p 和[ ]结合,10说明有是个元素,每个元素的类型是int*.

指针数组存放的是数组的地址,那么数组的地址又是怎么表示的?且看下面讲解:

数组名的本质

       一般情况下,数组名表示的就是首元素的地址,例如int arr[10] = {0};这里&arr[0]和arr 是一样的,我们可以验证一下:

指针进阶(上)_第5张图片

但是也有特殊情况,有两种:

sizeof(arr) //这里的arr表示取出整个数组,sizeof 对整个数组进行 arr 的大小计算,arr在这里就不是首元素的地址。
&arr //这里的arr表示取出整个数组,拿到的是整个数组的地址,但是表示的时候还是使用首元素的地址。

那各位可能就有疑惑了,&arr和arr又有什么区别呢?

大家先来看看下面的代码:

指针进阶(上)_第6张图片

这里的加1操作之后得到的地址又是不一样的,因为arr表示首元素的地址,因此加一操作后就是跳过一个元素,&arr取出的是整个数组,加一操作后跳过的是整个数组,所以它们在进行运算的时候会有所不同。

一维数组传参的本质

       在没有使用指针的时候,一维数组在进行传参时,我们都会去使用数组来接收:

void test(int arr[])  
{
    ...
}

int main()
{
    int arr[10]={0};
    test(arr);    //arr本质就是数组首元素的地址
    return 0;
}

在学习指针后,我们就可以使用指针来接受数组:

void test(int* parr)  
{
    ...
}

int main()
{
    int arr[10]={0};
    test(arr);    
    return 0;
}

有了指针之后,我们也可以通过指针来访问数组:

指针进阶(上)_第7张图片

这里我特意圈出来两个东西是 想说明:parr[i] 和 *(parr+i) 这两个是没有区别的,[ ] 和 * 都能起到解引用的作用。

既然大家对数组名已经有所了解了,那么我们也可以使用指针数组来模拟二维数组:

指针进阶(上)_第8张图片

解释一下代码,每个数组名变成了指针数组的元素,第一个[ ]是表示第几个元素,第二个方括号是这个元素中的第几个数字。
当然也可以这样来解引用:

*( *( p + i ) + j)

这里有一个小练习:

#include 

int main()
{
	char str1[] = "hello world!";
	char str2[] = "hello world!";
	const char* str3 = "hello world!";
	const char* str4 = "hello world!";
	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;
}
//说明运行结果是什么?

我们先上答案:

指针进阶(上)_第9张图片

解析:
在 str1 和 str2 中,“hello world!" 是这两个不同的数组存放的字符串的,因此这一个字符串被拷贝了两份分别放在str1 和 str2 这两个数组里面,即str1 和 str2 所保留的地址是不一样的;在str3 和 str4 这里时,"hello world!“就是作为一个常量,在计算机里相同的常量只会留下一份,这样也可以节省空间,所以str3 和 str4 他们所指向的都是同一个“hello world!”,这样的话,str3 和 str4 他们所保存的地址是一样的。

数组指针

       什么是数组指针,到底是数组,还是指针,我们来类比一下:整型指针是存放整型地址的指针,浮点型指针是存放浮点型的指针,字符指针是存放字符地址的指针…那么数组指针就是存放数组地址的指针,是指向数组的指针变量。
简单说明一下数组指针的样子:

int (*p)[10]

这里为什么要用()将* 和 p 括起来,因为 [ ] 的优先级要高于 * 的,所以要先保证 * 和 p 相结合。

我们来解读一下上面代码的意义:

指针进阶(上)_第10张图片

我们去掉变量名,剩下的int (*) [10] 就是指针的类型。

指针进阶(上)_第11张图片

二维数组的传参本质

       二维数组的数组名也是跟一维数组的数组一样,都是表示首元素的地址,那对于二位数组来说,首元素是什么呢?二维数组的首元素是一行的地址。
指针进阶(上)_第12张图片

大家来看一下应用吧:

指针进阶(上)_第13张图片

函数指针

       函数指针顾名思义就是指向函数的指针,存放的是函数的地址。

int (*p) (int,int) = Add;

如何解读:

指针进阶(上)_第14张图片

首先我们先来看一下函数名和取地址函数有什么不同:

指针进阶(上)_第15张图片

结果显而易见,&Add和Add没有任何区别。也就是说函数名其实表示的是这个函数的地址。

如何使用,其实很简单,就是更正常的函数传参一样,大家来看看代码吧:

指针进阶(上)_第16张图片

这里解不解引用都没问题的,因为上面我们就知道函数名可以代表函数的地址。

函数指针数组

       函数指针数组是数组,存放的是函数指针。

int ( *p[4] )(int , int) = {Add, Sub, Mul, Del};

如何解读这条代码:
指针进阶(上)_第17张图片

那么这到底有什么作用呢?大家来看看下面的转移表:

转移表

利用函数指针数组实现计算器功能。

#include 

void menu()
{
	printf("****************************************\n");
	printf("*****     1.加法       2.减法      *****\n");
	printf("*****     3.乘法       4.除法      *****\n");
	printf("*****     0.退出              **********\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 Del(int x, int y)
{
	return x / y;
}

void Tran(int n)
{
	printf("请输入两个操作数:");
	int x, y;
	scanf("%d %d", &x, &y);
	int (*pf[5])(int, int) = { 0,Add,Sub,Mul,Del };
	int ret = pf[n](x, y);
	printf("%d\n", ret);
}

       利用函数指针数组就是我们的代码变得简洁,如果一个一个去输入的话,代码会显得过于臃肿,在以后设计代码程序时,我们也可以利用一些巧妙的方法来去使我们的代码更加轻便。

你可能感兴趣的:(初始c语言,c语言)