目录
一、字符指针变量
二、数组指针变量
1.什么是数组指针变量?
2.数组指针怎么初始化?
三、二组数组传参的本质
四、函数指针变量
1.什么是函数指针变量?
2.函数指针变量使用
3.有趣代码
3.1typedef关键字
五、函数指针数组
六、转移表
总结
一、字符指针变量
字符指针变量的指针类型是char*;
一般使用:
int main()
{
char ch = 'w';
char* pc = &ch;
printf("%c\n", ch);
*pc = 'p';
printf("%c\n", *pc);
return 0;}
%c是打印字符的,%s是打印字符串的首地址
其他用法:
int main()
{
const char* p= "hello world";//常量字符串,是不能修改的
printf("%c\n", *p);
//*p = 'q';//err
return 0;}
很容易把代码hello world放在指针*p中,但是本质是把字符串的首地址放在*p中。
画图演示:
下面我们再来看看一段代码:
#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\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* p 指向字符的指针,存放的是字符地址
- 整型指针 char* p 指向整型的指针,存放的是整型地址
所以数组指针是一种指针变量,是存放数组地址的指针变量
char ch = 'w';
char* pc =&ch;//字符指针
int n =10;
int* p =&n;//整型指针
数组指针变量
int (*p)[10];
解释:p先和*结合,说明p是一个指针变量,然后指向的是一个大小为10个整型的数组
注意:[ ]的优先级要高于*,所以必须先加()保证p和*结合
2.数组指针怎么初始化?
在前面的学习中我们就知道,数组名是数组首元素的地址
int main()
{
int arr[10] = { 10 };
int(*p)[10] = &arr;//取出的是数组的地址
//p应该是数组指针
return 0;
}
&arr和p的类型是一致的
三、二组数组传参的本质
二维数组的一般写法
void test(int arr[3][5],int r,int c)
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < c; j++)
{
printf("%d ", arr[i][j]);
}
}
}
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;
}
二维数组名是谁的地址?
根据数组名是数组⾸元素的地址这个规则
- 二维数组的首元素就是第一行(一维数组)
- 每一行都是一个元素(一维数组)
画图演示:
void test(int (*p)[5], int r, int c)
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < c; j++)
{
//printf("%d ", (*p+i)[j]);
printf("%d ", *(*(p+i)+j));// arr[i]==*(arr+i)
}
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;
}
结论:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。
四、函数指针变量
1.什么是函数指针变量?
类比:
- 数组指针,指向数组,存放数组地址
- 整型指针,指向整型,存放整型地址
函数指针,指向函数,存放函数的地址
int Add(int a, int b)
{
return a + b;
}int* test(char* s)
{
return 0;
}
//
函数指针变量的写法和数组指针变量写法类似int main()
{
int*(*pt)(char) = &test;
int arr[8] = { 0 };
int(*pa)[8] = &arr;//pa是数组指针变量
int (*pf)(int,int) = &Add;//pf是函数指针变量
int x = 10;
int y = 20;
int z = Add(x,y);
printf("%p\n", &Add);
printf("%p\n", Add);
return 0;
}
&函数名和函数名都是表示函数地址
函数指针变量
int (*pf) (int x,int y);
解析:
2.函数指针变量使用
int Add(int a, int b)
{
return a + b;
}
int main()
{
int (*pf1)(int, int) = &Add;//pf就是函数指针变量
int (*pf2)(int, int) = Add;
int r1 = (*pf1)(3,7);
int r2 = (*pf2)(3,7);//*可以
int r4 = pf2(3,7);
int r3 = Add(3, 7);
printf("%d\n", r1);
printf("%d\n", r2);
printf("%d\n", r3);
printf("%d\n", r4);
return 0;
}
3.有趣代码
代码1:
void (*p)();
int main()
{
(*(void (*)())0)();//函数调用
//1.将0强制类型转换成void(*)()类型的函数指针
//2.调用0地址放的这个函数
return 0;
}
代码2
int main()
{
void (*signal(int, void(*)(int)))(int);//函数声明
//声明的函数名叫:signal
//signal的函数有2个参数,第一个参数的类型是int
//第二个参数的类型是void(*)(int)的函数指针类型,该指针可以指向一个函数,指向的函数参数是int,返回类型是void
// signal函数的返回类型是void(*)(int)的函数指针,该指针可以指向一个函数,指向的函数参数是int,返回类型是void
//void (*)(int)signal(int, void(*)(int));//err
return 0;
}
3.1typedef关键字
typedef对整型类型
typedef unsigned int uint;int main()
{
unsigned int num1;
uint num2;
return 0;
}
typedef对指针类型重命名
typedef int* pint;int main()
{
int* p1 = NULL;
pint p2 = NULL;
return 0;
}
数组指针重命名
typedef int(*parr_t)[5];
//*parr_t等价于int(*)[5]
int main()
{
int arr[5] = { 10 };
int(*p)[5] = &arr;//p是数组指针变量,p是变量的名字
//int (*)[5] -- 数组指针类型
parr_t p2 = &arr;
}
void test(char* s)
{}
//对函数指针类型重命名产生新的类型pf_t
typedef void(*pf_t)(char*);int main()
{
void(*pf)(char*) = test;
//void (*)(char*)
pf_t pf2 = test;
}
五、函数指针数组
类比:
- 字符指针数组 char* arr1[5];
- 整型指针数组 int* arr2[5];
如果要把多个相同类型的函数指针存放在一个数组中,这个数组就叫:函数指针数组
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 (*pf1)(int,int ) = add;
int (*pf2)(int,int ) = sub;
int (*pf3)(int,int ) = mul;
int (*pf4)(int,int ) = div;
int (*pfarr[4])(int, int) = { add,sub,mul,div };//pfarr就是函数指针数组
int i = 0;
for(i=0;i<4;i++)
{
int ret = pfarr[i](8,4);
printf("%d\n", ret);
}return 0;
}
int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };
解析:
六、转移表
我们这里写一个计算机,实现加减乘除法;
第一种写法:
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;
}
//想写一个计算器
//完成2个整数的运行
//1.加法
//2.减法
//3.乘法
//4.除法
void menu()
{
printf("**************************\n");
printf("*****1.Add 2.Sub******\n");
printf("*****3.Mul 4.Div******\n");
printf("***** 0.exit ******\n");
printf("**************************\n");
printf("**************************\n");
printf("**************************\n");
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
do
{
menu();
int input = 0;
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入2个操作数:");
scanf("%d %d", &x, &y);
ret = Add(x, y);
printf("%d", ret);
break;
case 2:
printf("请输入2个操作数:");
scanf("%d %d", &x, &y);
ret = Sub(x, y);
printf("%d", ret);
break;
case 3:
printf("请输入2个操作数:");
scanf("%d %d", &x, &y);
ret = Mul(x, y);
printf("%d", ret);
break;
case 4:
printf("请输入2个操作数:");
scanf("%d %d", &x, &y);
ret = Div(x, y);
printf("%d", ret);
break;
case 0:
printf("退出计算器\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}
我们可以看到选择这一部分是很冗余的,所以我们可以利用函数指针数组来解决。
第二种写法:
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;
}
//想写一个计算器
//完成2个整数的运行
//1.加法
//2.减法
//3.乘法
//4.除法
void menu()
{
printf("**************************\n");
printf("*****1.Add 2.Sub******\n");
printf("*****3.Mul 4.Div******\n");
printf("***** 0.exit ******\n");
printf("**************************\n");
printf("**************************\n");
printf("**************************\n");
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
//创建一个函数指针的数组
//转移表
int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };
do
{
menu();
int input = 0;
printf("请选择:");
scanf("%d", &input);
if (input >= 1 && input <= 4)
{
printf("请输入2个操作数:");
scanf("%d %d", &x, &y);
pfArr[input](x, y);
}
else if (input == 0)
{
printf("退出计算机\n");
}
else
{
printf("选择错误\n");
}
} while (input);
return 0;
}
这一种写法就看起来清爽很多,这样也可以添加更多的功能。
我们这里可以小结一下之前所学指针:
拓展:
char*(*(*p)[4])(int,char*) = &pfArr;//取出的是函数指针数组的地址
//p就是指向函数指针数组的指针;
所以我们就有第三种写法:
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;
}
想写一个计算器
完成2个整数的运行
1.加法
2.减法
3.乘法
4.除法
void menu()
{
printf("**************************\n");
printf("*****1.Add 2.Sub******\n");
printf("*****3.Mul 4.Div******\n");
printf("***** 0.exit ******\n");
printf("**************************\n");
printf("**************************\n");
printf("**************************\n");
}
void calu(int (*pf)(int,int))
{
int x = 0;
int y = 0;
int ret = 0;
printf("请输入2个操作数:");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("%d", ret);
}
int main()
{
int input = 0;
do
{
menu();
int input = 0;
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
calu(Add);
break;
case 2:
calu(Sub);
break;
case 3:
calu(Mul);
break;
case 4:
calu(Div);
break;
case 0:
printf("退出计算器\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}
第二种是将函数指针数作为跳板、第三种写法我们都是将 取出函数指针数组的地址作为跳板
总结
指针的第三节主要是字符指针变量、数组指针变量、二维数组传参的本质、函数指针变量、函数指针数组以及转移表。