万字深入理解C语言中的指针(3)

1. 字符指针变量

我们知道在指针中有一种类型是字符指针char*,一般使用:

#include<stdio.h>
int main()
{
  char a='w';
  char * p=&a;
  * p='w';
  return 0;
}

还有一种使用方法:

#include<stdio.h>
int main()
{
  const char*p="hello";
  printf("%s\n",p);
  return 0;
}

代码const char*p=“hello” 很容易被理解为把字符串"hello"存放在了字符指针p里面了,其实它的本质是把字符串的首个字符的地址存放在了p里面。上面代码的意思就是把一个常量字符串的首字符’h’的地址存放在指针变量p中。
再《剑指offer》中有一道和字符串相关的笔试题,我们一起来了解一下。

#include<stdio.h>
int main()
{
  char str1[]="hello bit.";
  char str2[]="hello bit.";
  const char * str3="hello bit.";
  const char * str4="hello bit.";
  if(str1==str2)
     printf("str1 and str2 are same");
  else
     printf("str1 and str2 are not same");
  if(str3==str4)
     printf("str3 and str4 are same");
  else
     printf("str3 and str4 are not same");
  return 0;
}

在这里插入图片描述
这⾥str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域,
当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始
化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。

2. 数组指针变量

2.1 什么是数组指针

之前我们提到了指针数组,指针数组是一种数组,用来存放地址(指针),数组指针变量究竟是指针变量还是数组呢?它是指针变量。我们已经熟悉:
整形指针变量: int * p;存放的是整形变量的地址,能够指向整形数据的指针。
浮点型指针变量: float * pf;存放的是浮点型变量的地址,能够指向浮点型数据的指针。
那数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量。
下面的代码那个是数组指针呢?

int *p1[10];
int (*p2)[10];

p2是数组指针。解释一下:p先和 * 结合,说明p是⼀个指针变量变量,然后指着指向的是⼀个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫指针数组。这里要注意[]的优先级要高于 * 符号,必须要加上()来保证p和*先结合。

2.2 数组指针变量如何初始化

数组指针变量是用来存放数组地址的,那么怎么获得数组的地址呢?就是我们之前学的&数组名。

int arr[10]={0};
&arr;//得到的是数组的地址

在这里插入图片描述
我们调试监视可以发现&arr和p的类型是一样的。int(p指向的数组的元素类型)
(* p) 10=&arr.

3. 二级指针传参的本质

有了对数组指针的理解,我们就能够讲一下二维数组传参的本质了。之前有⼀个⼆维数组的需要传参给⼀个函数的时候,当时是这样写的:

#include<stdio.h>
void test(int arr[3][5],int a,int b)
{
  int x=0;
  int y=0;
  for(int x=0;x<a;x++)
  {
    for(int y=0;y<b;y++)
    {
      printf("%d",arr[x][y]);
    }
    printf("\n");
  }
}
int main()
{
  int arr[3][5]={{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};
  test(arr,3,5);
  return 0;
}

这里的实参是二维数组,形参也写成二维数组的形式,那么还有其他的写法吗?首先我们再次了解一下二维数组,二维数组起始可以看成每个元素是一维数组的数据,也就是二维数组每个元素都是一个一维数组,那么二维数组的首元素就是第一行,也是一个一维数组:
在这里插入图片描述
所以,根据数组名是数组首元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀行的地址,是⼀维数组的地址。根据上⾯的例⼦,第⼀行的⼀维数组的类型就是 int [5] ,所以第⼀⾏的地址的类型就是数组指针类型 int(*)[5] 。那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。如下:

#include<stdio.h>
void test(int (*p)[5],int a,int b)
{
  int x=0;
  int y=0;
  for(int x=0;x<a;;x++)
  {
    for(int y=0;y<b;y++)
    {
      printf("%d",(*(p+x)+y);
    }
    printf("\n");
  }
}
int main()
{
  int arr[3][5]={{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};
  test(arr,3,5);
  return 0;
}

总结:二维数组传参,形参部分可以写成数组形式,也可以写成指针形式。

4. 函数指针变量

4.1 函数指针变量的创建

什么是函数指针变量?根据前面提到整形指针,数组指针的时候,根据类比关系,我们不难得出结论:函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。那么函数是否有地址呢?多的不说上调试!

#include<stdio.h>
void test()
{
  printf("hi\n");
}
int main()
{
  printf("test:%p\n",test);
  printf("&test:%p\n",&test);
  return 0;
}

在这里插入图片描述
根据这个调试结果,我们发现确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过&函数名获得函数的地址,

void test()
{
  printf("hi\n");
}
void (*p1)()=&test;
void (*p2)()=test;
int Add(int x,int y)
{
  return x+y;
}
int (*p3)(int,int)=Add;
int (*p4)(int x,int y)=&Add;

4.2 函数指针变量的使用

通过函数指针调用指针指向的函数。

#include<stdio.h>
int Add(int x,int y)
{
  return x+y;
}
int main()
{
  int (*p3)(int,int)=Add;
  printf("%d\n",(*p3)(2,3));
  printf("%d\n",p3(3,5));
  return 0;
}

在这里插入图片描述

4.3 typedef关键字

typedef关键字是用来给类型重命名的,可以将复杂的类型简单化处理。比如我们觉得unsigned int写起来不方便,如果我们能将它写成uint那就方便多了,那么我们可以使用:

typedef unsigned int uint;//将unsigned int初始化为uint

那如果它是指针类型,我们还能否将它重命名?答案是可以的,比如我们将int*重命名为ptr,我们可以这么写:

typedef int* ptr;

对于数组指针和函数指针会有区别,比如我们将数组指针int(*)[5]重命名为pr,那他应该这样写:

typedef int(*pr)[5];//新的类型名必须要放在*右边

函数类型的指针重命名也是一样的,比如将void(*)(int)类型重命名为pf,那么同理他应该这样写:

typedef void(*pf)(int);//新的类型名必须要放在*右边

5. 函数指针数组

数组是一个存放相同数据类型的存储空间,我们已经知道了指针数组,例如:

int *arr[10];//数组中每个元素的类型都是int*

那要把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (*parr1[3])();
int *parr2[3]();
int (*)()parr3[3];

上面代码块中,正确给函数指针数组定义的是parr1,parr先和[]结合,说明parr是数组,数组的内容是int(*)()类型的函数指针。

6. 转移表

这里我们来说明一下函数指针数组的用途:转移表.
例子:计算器的一般实现

#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;
}
int main()
{
  int x,y;//输入要进行计算的数
  int input=1;//根据input的值来判断进行哪种运算
  int ret=0;//计算之后的到的结果
  do
   {
     printf("**********************\n");
     printf("** 1. add  2. sub  ***\n");
     printf("** 3. mul  4. div  ***\n");
     printf("**      0. exit    ***\n");
     printf("**********************\n");
     printf("请选择:");
     scanf("%d",&input);
     switch(input)
      {
        case 1:
          printf("请输入数字:");
          scanf("%d%d",&x,&y);
          ret=add(x,y);
          printf("结果为:%d",ret);
          break;
        case 2:
          printf("请输入数字:");
          scanf("%d%d",&x,&y);
          ret=sub(x,y);
          printf("结果为:%d",ret);
          break;
        case 3:
          printf("请输入数字:");
          scanf("%d%d",&x,&y);
          ret=mul(x,y);
          printf("结果为:%d",ret);
          break;
        case 4:
          printf("请输入数字:");
          scanf("%d%d",&x,&y);
          ret=div(x,y);
          printf("结果为:%d",ret);
          break;
        case 0;
          printf("退出计算器");
          break;
      }
   }
  while(input)
  return 0;
}

使用函数指针数组来实现:

#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;
}
int main()
{
  int x,y;
  int input=1;
  int ret=0;
  int (*p[5])(int x,int y)={0,add,sub,mul,div};
  do
   {
     printf("**********************\n");
     printf("** 1. add   2. sub ***\n");
     printf("** 3. mul   4. div ***\n");
     printf("**      0. exit    ***\n");
     printf("**********************\n");
     printf("请选择运算方式:");
     scanf("%d",&input);
     if(input<=4&&input>=1)
      {
        printf("请输入数字:");
        scanf("%d%d",&x,&y);
        ret=(*p[input])(x,y);
        printf("运算结果为:%d",ret);
      }
     else if(input==0)
      {
        printf("退出计算器");
      }
     else
      {
        printf("无效选择");
      }
   }
  while(input);
  return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

野生的编程萌新

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值