指针数组:
int *n[];
由于 [ ] 的运算级别要比 * 高,因此这里的声明变量应当按照从右往左的顺序,即先确定n是一个数组, 然后这个数组内的元素是指针类型的,指针指向的数据类型为 int。因此这里声明的是一个int类型的指针数组。
注意,*n[ ]存放的是一个一维指针(一维数组名),**n[ ]存放的是二级指针(二级数组名)。不是*n[ ][ ],因为n不是二维数组。
存放后,指针数组的元素就可以看作是存进去的一维或二维指针来使用。如:
int a=10;
int b[][2]={30,50},*p1=&a,**p2=&p1;
int **p[]={p2};
a=**p[0]*5;
printf("%d",a);
代码示例:
(一)、
#include <stdio.h>
int main(void)
{
char tBooks[] = {'A'};
char *p[4] = {&tBooks[0]};
//tBooks[0]是一个具体元素,&tBooks[0]是一个一级指针
printf("请打印:\n%c\n",*p[0]);
// p[0]保存了&tBooks[0],即保存了具体元素的地址,所以p[0]是一个一级指针,*p[0]就是'A'
}
(二)、
#include <stdio.h>
int main(){
char *tBooks[] = {"《数据结构》","《计算机组成原理》","《C语言程序设计》","《计算机网络》","《哆啦A梦》"};
char **pt=&tBooks[4];
char **p[4] = {&tBooks[4]};
printf("%s\n",tBooks[4]); //打印出《哆啦A梦》
printf("请打印:%s\n",*pt); //打印出《哆啦A梦》
//tBooks[0]是一个一级指针,pt是保存其变量的二级指针,那么*pt就是tBooks[0]
printf("请打印:%s\n",*p[0]); //打印出《哆啦A梦》
/*
tBooks[4]是一个一级指针,&tBooks[4]是其地址也是一个二级指针,
&tBooks[4]保存于p[0],p[0]==&tBooks[4],则p[0]就是二级指针,*p[0]就是指向字符串的一级指针,可直接%s输出
*/
}
数组指针:
int (*n)[];
由于 () 的优先级要比 [ ] 高,因此这里的声明顺序从左到右,即先确定n是一个指针,然后它指向的数据类型是数组类型的,可以是一维数组,也可以是二维数组。且数组内的数据类型是 int 类型的。
假设定义 int (*p2)[5] = { 0, 0, 0, 0, 0}; 那么p2就指向了这个包含5个int类型的数组了。
例如:
#include <stdio.h>
int main(){
int temp[5] = {1, 2, 3, 4, 5};
int (*p)[5] = &temp; // 从此,(*p)等价于temp
int i;
for(i = 0; i < 5; i++)
{
printf("%d\n", *(*p + i));
//或者 printf("%d\n", (*p)[i]);
}
return 0;
}
1)第一行定义了含有5个int类型的数组temp;
2)第二行就定义了一个指向含有5个int类型数组的指针p,并把temp数组的首地址给了指针p。
注意:这里为什么不直接用 int (*p)[5] = temp;呢?
这是因为虽然temp和&temp的值都是相同的,但它们的意义却是不相同的:
*** temp指的是这个数组的 第一个元素 的首地址。
*** &temp 指的是这 整个数组 的首地址。
3)for循环是想把数组temp里面的数字都打出来。里面为什么用*(*p + i)呢?
p所指的是数组temp的地址,
*p+i 所指的是它在数组中的位置,
*(*p + i)就表示相应位置上的值了。
4)如果想指向二维数组的temp,则数组指针的定义要变成(*p)[ ][x]=&temp;
x为要标明的第二维长度。使用方法:(*p)[i][j]。
5)注意 定义二维数组的行指针 和 定义二维数组的数组指针 是不同的。
如有二维数组a[4][5];
该二维数组的行指针:
int (*pa)[5]; // 定义一个行指针,指向二维数组的每一行,一行(一个一维数组 / 二维数组的第二维)有5给元素
pa=a;
// 行指针与指向一维数组的数组指针一样,因为行指针就是指向一维数组的数组指针,他俩都是指向一维数组
该二维数组的数组指针:(*pa)[i][j];
行指针的使用方法:pa[i][j]; 数组指针的使用方法:(*pa)[i][j];
行指针自加一则变化一行,其实理解起来挺简单的,因为现在pa==a,而pa+i是指向下标为i的那一行的第一个元素。
-------------------------- *************** --------------------------
// 不能*p[1],因为[ ]会先执行,但p不是数组,会报错
***** 注意:因为数组名是一个指针常量,是一个常量,不是(一级)指针变量,对其取址并赋值给数组指针,相当于是把一个数的地址给了数组指针,所以指向数组的指针是一个一级指针。*****
***** 指针数组的数组名可以理解成一个二级指针。*****
返回指针的函数:
int *f()表示定义一个名为f的函数,这个函数的返回值是一个int类型的指针。
函数指针:
int(*f)(int)表示这是一个指向函数的指针变量,该函数的返回值类型为int,且具有一个int类型的形参。一般不用写形参,即int (*f)();即可。它要指向一个函数才能有用,指向一个函数之后可以用它来代替该函数。之后使用这个指针相当于使用该函数。
与数组类似,在数组中,数组名即代表着该数组的首地址,类似地,函数名实际上也是执行这个函数任务的代码在内存中的起始地址。因此,函数名就是该函数的函数指针。
因此,我们可以采用如下的初始化方式:
函数指针变量 = 函数名;
同理,我们在用函数指针调用函数时,语法为:
函数指针变量(形参); (就函数指针特殊,其他的均符合我们平常的认知)
其实,函数指针也可以通过“函数指针变量 = &函数名;”的方式来定义,但一般不会这么做,那是历史原因遗留下来的语法。
例如:
int K(int i){}
int(*f)(int)=K; // 等价于f=K。不用f=&K,因为函数名K就是一个地址。
// 但f(1);即可调用函数,不能*f(1); 与int *p2; p2=p1;一致。
函数指针数组:
int (*parr[3])(int,int);
// parr先与 [ ]结合,说明是一个数组,再与*结合,说明数组存放的是指针,指针都指向一个形参为两个int类型,返回值也是int类型的一个函数。
用法:
#include <stdio.h>
int add(int a,int b){
return a+b;
}
int sub(int a,int b){
return a-b;
}
int mul(int a,int b){
return a*b;
}
int div(int a,int b){
return a/b;
}
void make_menu(){
printf("****************************\n");
printf("请选择菜单:\n");
printf("1:加 2:减 3:乘 4:除 0:退出 \n");
}
/*定义函数指针数组变量
(int,int) 对应于函数指针数组指向的函数列表
*/
int (*fun_array[4]) (int,int) = {add, sub, mul, div};
// 函数指针数组内存放的都是同一形参数量及类型、同一返回值类型的函数指针
// 函数名本身就是一个函数指针
int main(){
int i,j;
int cmd;
while(1){
make_menu();
scanf("%d",&cmd);
if(cmd==0){
break;
}
if(cmd>=1&&cmd<=4){
printf("请输入2个数字:");
scanf("%d%d",&i,&j);
//通过函数指针数组去调用对应的函数
int result = fun_array[cmd-1](i,j);
//也可以 int result = (*fun_array[cmd-1])(i,j);
//同函数指针也可以通过“函数指针变量 = &函数名;”的方式来定义,
//但一般不会这么做,那是历史原因遗留下来的语法。
//也可以通过函数指针变量来调用对应的函数
//int (*p)(int,int) = fun_array[cmd-1];
//int result = p(i,j);
printf("result:%d\n",result);
}
}
return 0;
}
指向 指针数组 的指针:
定义方式:
char *tBooks[] = {"《数据结构》","《计算机组成原理》"};
char *((*p)[]) = &tBooks;
//(*p)表示p是一个指针,后面配合[]说明他是一个指向数组的指针,把 (*)[] 再 *() 一下表示指向的这个数组是一个指针数组。
//注意,这种命名方式是错误的:char **p[] = &tBooks;
那我们指针的赋值也完成了,该如何通过新定义的指针p取得想要的字符串"《计算机组成原理》"呢?
首先我们得知道,只需要取得 新定义的指针p 指向的指针数组tBooks 的第四个指针元素 即可,因为 %s会帮我们自动打印。
对p解引用,即(*p),取得p存储的地址对应的元素的数据,p是四级指针,刚刚我们让它存储了三级指针tBooks的地址,所以 *p 等价于 tBooks。
有了这个三级指针tBooks,我们就可以对这个三级指针tBooks进行指针运算。当我tBooks+1就代表地址加一个单位,就是tBooks数组第二个指针元素的地址(注意别又在这犯错误了,数组名字加减运算所得到的是元素的地址,而不是元素内所保存的内容,我们选择要取的是元素内容,因为元素内容就是指向字符串的指针。所以我们想要的是tBooks[1], [ ]相当于是对tBooks这个元素的地址进行取值操作,等价于*() 。)
而后我们使 tBooks+1 再解引用,即*(tBooks+1) 写法同 tBooks[1],就可以拿到tBooks的第二个指针元素内的存储的一个一级指针,该一级指针内保存的的字符串常量的首地址。我们拿到这里就够了。我们就需要这个字符串的地址,至于里面的字符,%s会帮我们自动打印。
那么综上所述,这一系列的取地址实际上就是先对四级指针p进行解引用(*p)就得到三级指针所存放的数据,是一个地址(即数组tBooks的首地址)对此地址进行运算+1 即(*p)+1 ,得到的是数组tBooks第二个元素的地址,再次解引用,即 *((*p)+1),得到运算后的地址就是tBooks数组第二个元素存放的数据(因为tBooks是指针数组所以这里的数据还是地址),即字符串的地址,搞定。
printf("请打印:%s",*(*p+1));
指向 函数指针数组 的指针:
#include<stdio.h>
void test(const char *str)
{
printf("%s\n", str);
}
int main()
{
void (*pfun)(const char*) = test;
//函数指针pfun
void (*pfunArr[5])(const char* str);
//函数指针的数组pfunArr
void (*(*ppfunArr)[10])(const char* str) = &pfunArr;
/*
(*ppfunArr)表示ppfunArr是一个指针,后面配合[]说明他是一个指向数组的指针,把 (*ppfunArr)[10] 再 *() 一下表示指向的这个数组是一个指针数组。(类似于指向指针数组的指针的命名方式)
*/
//指向函数指针数组pfunArr的指针ppfunArr
return 0;
}