目录
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;
}