目录
先来回忆一下指针概念:
1. 指针就是变量,用来存放地址,地址唯一标识一块内存空间。2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3. 指针有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作时的权限。
一、字符指针
字符指针类型————char*
存放字符地址:
char ch = 'w';
char* pc = &ch;
存放字符串首字符地址:
char* p = "abcdef";
加上const:
const char* p = "abcdef";
此时*p不能作任何修改。
《剑指offer》中的一道和字符串相关的题:
#include <stdio.h>
int main()
{
char str1[] = "hello world.";
char str2[] = "hello world.";
const char *str3 = "hello world.";
const char *str4 = "hello world.";
if(str1 ==str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 ==str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
打印结果为:
原因:
所以str1 != str2,str3 == str4。
二、数组指针
1).概念
字符指针——存放字符地址——指向字符——char*
整型指针——存放整型地址——指向整型——int*
浮点型指针——存放浮点型地址——指向浮点型——float*,double*
数组指针——存放数组地址——指向数组
int main()
{
char ch = 'w';
char* pc = &ch;
int a = 10;
int* pi = &a;
int arr[10] = { 0 };
int (*pa)[10] = &arr;
return 0;
}
注意:1. []的优先级高于*号,必须加上()来保证p先和*结合。
2. 指针接收的是&arr,不能直接写arr。
因为:
数组的地址(&arr)和数组首元素地址(arr/&arr[0])从值的角度来看相同,但是意义不同。
2).应用(二维数组)
有一个二维数组的需要传参给一个函数的时候,可以这样写的:
这里实参,形参都是二维数组的形式。
还可以这样写:
二维数组的数组名表示的就是第一行的地址,是一维数组的地址。根据上面的例子,第一行的一维数组的类型就是型就是数组指针类型 int [5],所以第一行的地址的类型 int(*)[5] 。那就意味着二维数组传参本质上也是传递了地址,传递的是第一行这个一维数组的地址。
总结:二维数组传参,形参的部分可以写成数组,也可以写成指针。
三、函数指针
1).概念
函数指针——存放函数地址——指向函数
函数指针变量用来存放函数地址,未来通过地址能够调用函数
pf就是函数指针,存放函数地址 。同时,也可以通过函数地址来调用函数:
所以函数是有地址的,函数名就是函数的地址,也可以通过&函数名的方式获得函数的地址。
函数指针类型解析:
再来看两句代码(出自:《C陷阱和缺陷》):
1 (*(void (*) () )0)();
该代码是一次函数调用,首先将0强制类型转换为void ( * ) ( )的函数指针,然后*解引用调用0地址处的函数
2 void (*signal(int , void(*)(int) ) )(int);
该代码是一次函数声明,声明的函数名是signal,该函数的参数类型分别为int,void (*) (int),返回类型是函数指针类型,为void (*) (int)
该代码还可以这样写,便于理解:
2).函数指针数组
数组是一个存放相同类型数据的存储空间,我们已经学习了指针数组, 比如:
int * arr[10];
//数组的每个元素是int*
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,如何定义函数指针数组?
int (*parr1[3])();
int *parr2[3]();
其中parr1表示函数指针数组,parr1 先和 [] 结合,说明parr1是数组,元素类型为int (*) ( )。
指向函数指针数组的指针:
拆解一下: (参考数组指针)
3).应用
1. 通过函数指针调用函数
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf3)(int, int) = Add;
printf("%d\n", (*pf3)(2, 3));
printf("%d\n", pf3(2, 3));
return 0;
}
2. 转移表(函数指针数组)
以计算器为例,一般实现:
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;
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("ret = %d\n", ret);
break;
case 2:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
使用函数指针数组的实现:
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("ret = %d\n", ret);
}
else if (input == 0)
{
printf("退出计算器\n");
}
else
{
printf("输⼊有误\n" );
}
} while (input);
return 0;
}
p数组里有5个元素,每个元素都是int(*)(int, int)类型,通过修改input的值来访问p数组中的元素,也就是调用函数,(修改下标)。
完,感谢阅读。