今日伊雷娜:
目录
前言:
1、字符指针变量
1.1 使用字符指针存放字符
1.2 使用字符指针变量存放字符串
2、数组指针变量
2.1 什么是数组指针变量?
2.2 数组指针变量初始化
2.3 关于数组指针类型的解析
3、函数指针变量
3.1 函数地址
3.2 函数指针变量的创建
3.3 关于指针的连续定义
3.4 函数指针变量的使用
3.5 补充函数调用
前言:
本篇文章涉及字符指针变量,数组指针变量,函数指针变量,以及函数指针数组。
1、字符指针变量
在指针类型中我们知道有一种指针类型叫char*
1.1 使用字符指针存放字符
#include <stdio.h>int main(){char ch = 'l';char* pc = &ch;//取出ch的地址放到ch里*pc = 'l';return 0;}
pc为字符指针变量,指针变量是变量,存放的是字符,类型是char*
关于字符指针变量还有另外一种用法。也就是存放字符串。
1.2 使用字符指针变量存放字符串
char* p = "JonlyMay";//字符指针p指向了一个字符串
这句代码的意思是什么呢?是把 "JonlyMay"字符串存放到指针变量p 中吗?
重点:p 中存放的是该字符串首字符的地址
我们来与数组类比理解 char* p = "JonlyMay"中是将首元素地址赋给字符指针变量p:
char arr[ ] = "JonlyMay";
char* p = arr
我们创建了一个字符数组arr,并把数组首元素地址arr赋给了p,p变量里存放了数组首元素地址
我们可以用代码再来理解一下
#include <stdio.h>int main(){char arr[] = "JonlyMay";printf("%c\n", arr[2]);printf("%c\n", "JonlyMay"[2]);return 0;}
可以把 "JonlyMay"想象成一个数组,[2]就访问下标为2的元素
const char* p = "JonlyMay" //这里加上const的原因是"JonlyMay" 是常量字符串,不能被修改的
其实仔细想想也不可能是将整个字符串都存放到指针变量p中,空间也不够啊!
p如果在x86的环境下,只能向内存申请4个字节的空间,而JonlyMay+\0有九个字节也放不下!
总结:
const char* p = "JonlyMay" 这串代码的意思是:把一个常量字符串首字符 J 的地址存放到指针变量 p 中。
2、数组指针变量
2.1 什么是数组指针变量?
在介绍数组指针变量之前,我们需要先提一下字符指针变量,整型指针变量,那这哥两是什么意思呢?
(pc)字符指针变量是一种变量,里面存放的是(&ch)字符指针 (地址)
char ch;
char* pc = &ch;
(pn)整型指针变量是一种变量,里面存放的是(&n)整型指针(地址)
int n ;
int* pn = &n;
通过与它们的类比,我们应该能得出数组指针变量也是一种变量,里面存放的是数组指针(地址)
现在我们了解了指针数组指针变量的基本作用,那数组指针(地址) 怎么得到呢?
比如说 int arr[10] ,我们怎么得到数组的地址呢?
arr,&arr[0]:表示首元素地址;
&arr:表示的是数组的地址
数组的地址是 通过取地址数组名得到的(&arr)
也就是说数组指针变量就是用来存放&arr的
我们知道
字符指针变量的类型是字符指针(char*);
整型指针变量的类型是整型指针(int*),
那数组指针变量(parr)的类型是什么呢?是数组指针吗?好像是的,但是应该怎么表达呢?
我们举一个整型数组的例子
int main(){int arr[10] = { 0 };int* parr[10] = &arr;return 0;}
乍一看似乎没毛病,但是在int* parr[10] = &arr中 int* 就变成了整型指针, parr和[10]结合变为数组了,parr是数组名,那int* parr[10] = &arr的意思就变成了一个名为parr的数组有10个元素,每个元素类型为int*。
但是我们想要的parr是一个指向数组的指针,应该怎么做呢?
其实这里面涉及到了操作符优先级的问题 ,如果对操作符优先级不太了解的,可以看一看博主关于操作符的博文:C:操作符介绍-学习笔记-CSDN博客
int* parr[10] = &arr,在这串代码中,parr先和[10]结合变成了数组,我们希望得到的是一个指针变量parr,而当parr和[10]结合后就变为了数组名,得不到想要的结果。主要原因是(*)操作符的优先级低于([ ])操作符
说了这么多,怎么解决呢?其实很简单,我们只需要使用圆括号()将 * 和 parr括起来就可以了
从上面的图中我们可以看到圆括号的优先级是最高的,所以当我们想得到一个指针,就需要* 和 parr先结合,而不是parr和[10]先结合
表达形式:int (*parr) [10] = &arr;
parr与*结合说明parr是指针,指针指向的是数组 [10]说明数组有10个元素,每个元素类型是int
现在这里的parr就是数组指针变量。
现在关于数组指针变量的表达形式我们已经很清楚了
那数组指针变量的类型是什么?int(*)[10]
int* pn = &n;中pn的类型就是int *
char* pc = &ch;中pc的类型就是char*;
所以变量的类型就是去掉变量名字,剩下的就是变量类型
int (*parr) [10] = &arr中parr的类型就是 int(*)[10],&arr的类型也是 int(*)[10]。
来举一个稍微有点难度的例子来帮助我们更好的掌握数组指针变量类型
胡言乱语:写到这突然想到一个很有意思的两句话,你已经学会加减乘除了,来,写道高数题练练手。
哈哈哈哈哈哈哈哈哈。(作者日常发癫,不必理会)言归正传,来看一下题目
int* arr[9] = { 0 };
p = &arr;
问:这个p的类型是什么?
答:int*(*)[9]
首先,p先于*结合(*p),然后指向数组arr,有9个元素(*p)[9] ,数组中每个元素类型是int*,
所以变为 int*(*p)[9] 。所以数组指针变量p的类型就是int*(*)[9]
不知道大家是否还记得在这篇C:指针和数组之间的关系-学习笔记-CSDN博客文章中关于&arr+1后跳过了40个字节
相信到这里应该能够理解为什么跳过40个字节了吧!
&arr的类型是数组指针,int(*)[10],数组指针认为指向的数组有10个元素,每一个元素类型都是int。所以+1后跳过了4*10个字节。
总结:数组指针变量是用来存放数组的地址,数组的地址通过&数组名获得,将数组地址存起来放到数组指针变量中,数组指针变量的类型就是( 数组元素类型(*)[元素个数] )
2.2 数组指针变量初始化
关于数组指针变量的初始化有两种方式:
1、直接使用数组的地址进行初始化
例如对一个整型数组int arr[10],可以这样初始化;
int (*p)[10] = &arr;
2、先声明数组指针变量,然后在后续的代码中通过赋值来初始化。
int (*p)[5]; p = &arr;
2.3 关于数组指针类型的解析
数组指针变量是一种特殊类型的指针,它指向的是整个数组,而不是单个元素。
数组指针变量的声明形式通常为 数据类型 (*指针变量名)[数组大小]
例如 int (*p)[5] 声明了一个指向包含 5 个整数的数组的指针 p 。
通过数组指针访问数组元素时,通常需要结合下标来进行。
对于上述的 p,(*p)[0] 表示数组的第一个元素,(*p)[1]表示第二个元素,以此类推。
图文总结:
int (*p) [10] = &arr; | | | | | | | | p指向数组的元素个数 | p是数组指针变量名 p指向的数组的元素类型
3、函数指针变量
3.1 函数地址
通过前面的学习我们知道变量可以取地址,数组也可以取地址。那么想问大家一个问题,函数有地址吗?函数可以取地址吗? 答案是函数有地址
既然函数有地址,我们该怎么得到呢?
上图中,我们可以发现想要得到函数的地址可以通过&函数名和函数名两种方式得到函数的地址
注意:
对于函数来说,只有一个地址,这里不要和数组搞混了,数组中&数组名拿到的是数组首元素地址,但是函数不存在什么首元素地址。
3.2 函数指针变量的创建
既然我们知道函数的地址是怎么得到的时候,我们该怎么将函数地址存起来呢?
比如上面那个add函数,我们想将它存到pf中,该怎么表示呢?pf的类型该怎么写呢?
pf = &add;
这里可以类比数组指针变量,int (*p) [10] = &arr;
照猫画虎,这里小编先把 pf 的类型写出来
int (*pf)(int x, int y) = &add;
类比int (*p) [10] = &arr;理解int (*pf)(int x, int y) = &add;
首先, int (*p) [10] = &arr; 中,p是一个数组指针,它指向的是一个包含 10 个整数的数组。
而int (*pf)(int x, int y) = &add; 中,pf 是一个函数指针,它指向的是一个接收两个int 类型参数并返回 int 类型值的函数。
就如同数组指针 p 通过 &arr 获得了指向数组的地址,函数指针 pf 通过 &add 获得了指向函数 add的地址。
对于数组指针,通过 (*p) [i] 的形式可以访问数组中的第 i 个元素。
对于函数指针,通过 (*pf)(int x, int y) 的形式可以调用所指向的函数,并传递参数类型int ,int
注意:
(int x, int y)中形参的x y是可以省略的,只需要传递参数类型就可以了
pf就是函数指针变量
函数指针类型图解:
int (*pf) (int x, int y) | | | | | | | | pf指向函数的参数类型和个数的交代 | 函数指针变量名 pf指向函数的返回类型
3.3 关于指针的连续定义
如果是连续定义两个整数,我们可以这么写
int a , b;
那如果是来连续定义两个指针呢?我们还可以这么写吗?
int* a , b;
答案是不对的;我们可以来看一下
当我们将鼠标放到a上时,可以看到a的类型是 int*
但是当我们将鼠标放到b上时,可以看到b的类型是 int
所以,关于来纳许定义两个指针我们需要每一个变量都有*
正确表达形式:int *a , *b;
3.4 函数指针变量的使用
我们将函数地址存放起来,是为了后面的使用,那么我们应该怎么使用呢?
int (*pf)(int x, int y) = &add;
我们怎么调用这个函数呢?
#include <stdio.h>int add(int x, int y){return x + y;}int main(){int (*pf)(int x, int y) = &add;int ret = (*pf)(2, 3);//调用函数 printf("%d",ret);return 0;}
结果:
int ret = (*pf)(2, 3)解读:
pf 是一个函数指针,*pf 表示对函数指针进行解引用,得到它所指向的函数。
然后 (*pf)(2, 3) 就是调用这个函数指针所指向的函数,并向其传递参数 2 和 3。
最后,将函数的返回值赋给变量 ret 。
3.5 补充函数调用
还记得我们正常函数调用是怎么操作的吗?
#include <stdio.h>int add(int x, int y){return x + y;}int main(){int ret = add(2, 3);printf("%d", ret);return 0;}
int (*pf)(int x, int y) = &add;
int ret = (*pf)(2, 3);
这个是指我们先将函数地址存放到指针变量pf中去,然后我们通过函数指针变量来调用函数
我们来对比一下这两种调用方式
//第一种:int (*pf)(int x, int y) = &add;int ret = (*pf)(2, 3);printf("%d", ret);//第二种:ret = add(2, 3);printf("%d", ret);
前面我们说过add就是函数的地址,pf中也是函数的地址,那*pf为什么要解引用呢?是否可以直接使用呢?来测试一下
可以发现没有解引用pf也能直接使用,所以在函数指针调用的时候 * 是可以省略的 ,它是没有实际意义的。
这里写*只是为了方便理解。
4、函数指针数组
数组是用来存放相同类型数据的存储空间,在前面数组与指针这一篇中我们也介绍了指针数组的概念,如:
int * arr[10];//数组中的每个元素是int*
那要是把函数的地址存放到一个数组中,那么这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int main(){//指针数组int* arr[5]; //整型指针数组char* ch[5]; //字符指针数组return 0;}
如果我们将函数指针也放在数组中呢?
int add(int x, int y){return x + y;}int sub(int x, int y){return x - y;}int main(){int(*pa)(int, int) = add;int(*ps)(int, int) = sub;return 0;}
从上面代码中我们可以发现pa,ps的类型是一样的,既然如此,我们就可以将他们放到一个数组中,毕竟数组是用来存放相同类型数据的存储空间,那我们如何创建一个函数指针数组呢?
int ( * parr[4] )( int, int )
这样写parr就变为数组名了,有4个元素,然后指针为parr[4];
如果我们把parr[4]去掉再看剩下的部分int ( * )( int, int ) ,欸,这不是一个函数指针类型,也就是说我们创建的这个数组parr[4]的元素类型是函数指针,所以我们所创建的这个数组就是函数指针数组。
这个函数指针数组是怎么创建的呢?
我们只需要在函数指针变量的基础上,在后面加上一个 [元素个数],就可以表示函数指针数组了
这时候这个数组就可以存放一些函数指针了
int (*parr[4])(int, int) = { add,sub };
函数指针数组的作用:数组的创建时为了方便更好的数据管理,当我们想要存放多个函数的地址,并且函数返回类型统一,我们就可以造一个函数指针数组。
结语:本篇文章到这里就结束了,主要介绍了一些指针变量的用法,希望大家在看完这篇文章后能够有所收获!下篇再见啦!!!