详细解读指针(二)——访问函数的地址

二级指针

指针是变量,那么也就有相应的地址。指向指针地址的指针,叫做二级指针。

int a = 0;
int* p = &a;
int** p1 = &p;

在以上代码中,p1就是二级指针,它的类型为int**,它解引用一层,即*p1,能得到p,即它所指向的内容(虽然也是个指针)

同理可得,三级指针就是int***,以此类推

字符指针

字符指针类型为char*,但是它在存储数据的时候略有不同。

在这里插入图片描述

如以上代码所示,字符指针类型初始化时可以使用地址也可以使用字符串

  • 使用地址初始化时,使用方法和int相同
  • 使用字符串初始化时,相当于把首个字母的地址放入了这个指针变量里面

但是需要注意的是,用字符串形式输出时程序是会一直往后读取直到遇到\0为止,所以可能出现越界访问的情况,就如同以上的乱码

#include<stdio.h>

int main()
{
   char str1[]="abcd";
   char str2[]="abcd";
   char* str3="efgh";
   char* str4="efgh";
   if(str1 == str2)
      printf("1\n");
   if(str3 == str4)
      printf("2\n");
   return 0;
}

以上代码的结果是打印了一个2,也就是说str3和str4在值上是相等的

变量都具有两个属性:值属性和类型属性

首先str3初始化,指向了一块空间,该空间存放“efgh”这个字符串,然后创建str4,也指向“efgh”,此时就没必要再创建新的空间保存它了,直接指向了和str3一样的空间。所以str3和str4在值上是一样的(不过这俩是两个变量,地址还是不一样的)
此时efgh为字符串常量,是不可以修改的

而str1和str2都是开辟了两块不同的空间保存了两份一样的内容,在数组里的内容是可以修改的

数组指针

数组指针就是指向数组的指针,如int(*arr)[5],它的类型是int(*)[5]

  • 注意:[]的优先级高于*,所以要加括号保证*和arr先结合

二维数组如何传参?

函数中,数组的传参实际上是一种传址调用,调用的是数组首元素的地址

那么二维数组就是将首行(每一行看作一个一维数组)的地址传了过去

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

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

原来我们用这种方式接收传过来的数组

还可以:

void test(int(*arr)[5])//这是接收了首元素arr[0](一个一维数组)的地址
{
   //......
}

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

以上代码用了指针的形式接收了传过来的数组,其中的int(*arr)[5]中的arr实际上接收的是arr[0](一个一维数组的数组名,也是它的地址)

在二维数组中,数组名代表的是首行的地址,以上代码的arr+1表示跳过一行的元素

函数指针

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

#include<stdio.h>

void test(int a, int b)
{
   //......
}
int main()
{
   void(*pf)(int, int)=test;
//返回类型     参数类型
}

在以上代码中,pf就是一个函数指针,指向的是有两个int类型的参数,返回类型为void的函数

此处等号后面用test或者&test都可以,这两种都表示函数地址

有了函数地址就可以调用这个函数了

typedef函数

这个函数可以重命名一个类型

typedef unsigned int unint;

以上代码就是把unsigned int重命名成int

比如下面的代码:

( * (void (*)()) 0 )();

这个代码就是将0强制类型转换成void (*)()类型(一个函数指针类型)
然后再对0这个地址的函数进行调用(前面的*解引用可以省略)

对于指针类型重命名,新的名字要在*的右边

typedef void(*vd)();
( *(vd)0 )();
//函数地址 参数类型(没有)

这段代码和上面的是一样的

void (*signal(int, void(*)(int)))(int);

这段代码是在声明一个叫signal的函数
signal函数有两个参数,一个是int,一个是void( * )(int)类型的函数指针
signal的返回值是一个void ( * )(int)类型的指针

typedef void(*vd)(int);
vd signal(int, vd);

如果把void( * )(int)简化一下,这段代码就会好看很多

函数指针数组

函数指针数组是一个数组,用于存放一组函数(这些函数的参数类型和返回类型都要相同)

int (*p[4])={test1, test2, test3, test4};

这个数组名是p,去掉p等号前面就是它的类型,即int(*)[4]

转移表

转移表就是将一组相似的函数放在一个数组中,方便调用
例如简易计算器的实现:

int Div(int x, int y)
{
  return x / y;
}
int Mul(int x, int y)
{
   return x * y;
}
int Sub(int x, int y)
{
   return x - y;
}
int Add(int x, int y)
{
   return x + y;
}
int main()
{
   int i = 0;
   int (*p[5])(int x, int y)={0, Add, Sub, Mul, Div};
   do{
   printf("1.Add  ");
   printf("2.Sub  ");
   printf("3.Mul  ");
   printf("4.Div\n");
   printf("请选择模式:");
   scanf("%d", &i);
   if(i > 0 && i < 4)
      {
        int x = 0;
        int y = 0;
        printf("请输入操作数:");
        scanf("%d %d", &x, &y);
        int ret = p[i](x, y);
        printf("%d\n", ret);
      }
   }while(i);
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值