数组、指针与函数的梦幻联动

指针数组:

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;

}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秦矜

对你有帮助的话,请我吃颗糖吧~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值