
🎉个人主页: 缘三水的博客
❄专栏传送门:C语言专栏(新手向)
🎀人生格言:行动是迷茫的最好解药
🚀个人介绍:

往篇回顾
文章目录
- 往篇回顾
- (一)字符指针变量
- (二)数组指针变量
- (三)二维数组传参的本质
- (四)函数指针变量
-
- [4.1 定义](#4.1 定义)
- [4.2 函数指针的创建](#4.2 函数指针的创建)
- [4.3 函数指针的使用](#4.3 函数指针的使用)
- [4.4 两段有趣的代码](#4.4 两段有趣的代码)
- [4.5 typedef关键字](#4.5 typedef关键字)
- (五)函数指针数组
- (六)转移表
- 总结
正文开始
(一)字符指针变量
定义
指针变量类型为char*的变量
示例
c
char c = 'w';
char* pc = &c;//pc是字符指针变量
对于字符数组
c
char arr[] ="abcdef";
char* pc = arr;
也有另一种写法
c
char* pc = "abcdef";//"abcdef"是常量字符串,不能被修改
//pc中存放的仍然是首元素的地址
如果我们想打印字符串内容,可以这么写
c
printf("%c\n", *pc);//打印字符串的第一个字符
printf("%s\n", pc);//打印整个字符串
"abcdef"是常量字符串 ,不能被修改
如果强制修改,程序运行时就会报错
为了防止错误,我们一般在前头加上const修饰
c
const char* pc = "abcdef";
问题
下述代码运行会打印什么?
c
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\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;
}
结果是

对第一行解释
比较的是首元素地址,而不是数组的内容
数组名是代表首元素的地址
两个数组的首元素地址不同,因此打印这个结果
拓展:字符串比较不能用==,而是用strcmp函数
对第二行解释
当两个指针变量存储的是同一常量字符串 时
两个指针指向的位置都是这个字符串的首字符
因为常量字符串是不会被修改的,编译器就没必要再创建一个字符串来给第二个指针变量存放

(二)数组指针变量
类比
字符指针变量:存放的是字符的地址
整型指针变量:存放的是整型的地址
定义
数组指针变量:指向数组的指针,存放的是数组的地址
示例
c
int arr[10] = {0};
int (*p)[10] = &arr;//数组指针变量
// *P说明p 是指针变量
//[10]说明指向数组的地址
//int说明数组元素类型是int
//类型为int(*)[10]
区分
c
int* p[10] ={&arr};//指针数组,存放指针的数组
int (*p)[10] = &arr;//数组指针变量,存放数组地址的指针变量
去掉括号,p就和[10]结合,变成数组,p就变成数组名
数组指针的使用
c
//利用数组指针打印数组内容
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int (*p)[10] = &arr;
for (int i = 0; i < 10; i++)
{
printf("%d ", (*p)[i]);
}
return 0;
}
但是这种使用十分不利于阅读,因此不推荐这么使用
数组指针的使用场景一般在二维数组的传参中体现
(三)二维数组传参的本质
示例
打印一个二维数组的内容
c
void test(int arr[][5], int r, int c)
{
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
printf("%d ", arr[i][j]);
}
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;
}
之前我们学过,数组名代表首元素地址
而二维数组的元素其实是一维数组
因此,二维数组的数组名代表的是第一行一维数组的地址

所以,test函数的形参还可以这么写
c
void test(int (*arr)[5], int r, int c)
//*arr说明是指针变量
//[5]说明指向的数组有5个元素
//int说明指向的数组的元素类型是int整型类型
我们这里是用数组指针接收二维数组的首元素(第一行 一维数组的地址)
而打印的部分也能用指针来写
c
printf("%d ", arr[i][j]);// *(*(arr+i)+j) == *(arr[i]+j) == arr[i][j]
总结:二维数组传参,形参部分可以写成数组,也可以写成指针形式
(四)函数指针变量
4.1 定义
函数指针变量:存放函数的地址,指向的是函数
示例

c
取出函数的地址
&+函数名
不同于数组,数组名代表首元素地址
函数名 等价于 &函数名
4.2 函数指针的创建

c
(*pf)说明pf是指针变量
后面的(int,int)说明指针指向的是函数,形参类型分别是int类型,int类型
前面的int说明指向的函数返回值类型为int
函数指针类型
c
指针变量名去掉就是函数指针变量的类型
如:int (*)(int,int) 是函数指针变量pf的类型
4.3 函数指针的使用
示例

因为&Add 等价于 Add
所以pf 等价于Add
因此代码也可以这么写
示例2

4.4 两段有趣的代码
一段代码
c
(*(void (*)())0)();
该怎么解释这段代码呢?
c
这段代码在调用0地址处的一个函数
1. void(*)() 是一个函数指针类型,指向的函数没有参数,返回值类型是void
2. 外面加一个括号,( void (*)() )是将0强制类型转换成这种函数指针类型,意味着0地址处有这么一个函数
3. 最后,(*(void (*)())0)() 是对0地址处进行解引用,调用0地址处的函数
一段代码
c
void ( *signal(int , void(*)(int)) )(int);
解释
c
这段代码可以这么看
void ()(int) *signal(int , void(*)(int))但这么写是错的
1.这是一个signal函数声明
2.signal函数的参数类型是int,void(*)(int)
3.signal函数的返回类型是void ()(int)
4.5 typedef关键字
作用: 重命名数据类型
1.使用示例

2.指针类型的重命名
好处:可以连续创建指针变量
使用示例

3.数组指针类型的重命名
示例

4.函数指针类型的重命名
示例

所以,我们可以利用这个typedef对上面的第二段代码(函数声明)进行简化
原代码
c
void ( *signal(int , void(*)(int)) )(int);
优化代码

(五)函数指针数组
定义
存放同类型函数指针的数组
存放函数指针示例

使用函数指针数组示例

这样我们就利用函数指针数组一次完成了加减乘除四个函数的调用
(六)转移表
要求
写一个程序,能实现加减乘除的计算器
c
//实现一个能进行加减乘除的计算器
void menu()
{
printf("*********************\n");
printf("***1.add 2.sub***\n");
printf("***3.mul 4.div***\n");
printf("***0.exit ***\n");
printf("*********************\n");
}
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int r = 0;
do
{
menu();
printf("请输入:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入两个操作数");
scanf("%d %d", &x, &y);
r = Add(x, y);
printf("%d\n", r);
break;
case 2:
printf("请输入两个操作数");
scanf("%d %d", &x, &y);
r = Sub(x, y);
printf("%d\n", r);
break;
case 3:
printf("请输入两个操作数");
scanf("%d %d", &x, &y);
r = Mul(x, y);
printf("%d\n", r);
break;
case 4:
printf("请输入两个操作数");
scanf("%d %d", &x, &y);
r = Div(x, y);
printf("%d\n", r);
break;
case 0:
printf("退出计算器");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
我们发现这样的代码有许多重复的
也不利于功能的拓展
此时,我们就能使用函数指针数组进行优化
优化代码
c
void menu()
{
printf("*********************\n");
printf("***1.add 2.sub***\n");
printf("***3.mul 4.div***\n");
printf("***0.exit ***\n");
printf("*********************\n");
}
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
do
{
menu();
printf("请输入:>");
scanf("%d", &input);
if (input == 0)
{
printf("退出计算器\n");
}
else if(input > 0 && input <= 4)
{
int (*pArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };
printf("请输入两个操作数");
scanf("%d %d", &x, &y);
int r = pArr[input](x, y);
printf("%d\n", r);
}
else
{
printf("选择错误,请重新选择\n");
}
} while (input);
return 0;
}
这样以后想要拓展功能时,只要在菜单,函数功能说明,函数指针数组里和条件判断改变就可以了
总结
这篇文章我们详细介绍了指针部分内容,希望能对你有帮助
有错误的地方希望可以得到大佬的指正
最后不要吝啬你的点赞,收藏和评论!!!
感谢你的观看