文章目录
一、字符指针变量
二、数组指针变量
2.1 数组指针是什么
2.2 数组指针变量初始化
三、二维数组传参的本质
四、函数指针变量
4.1 什么是函数指针变量
4.2 函数指针变量的使用
4.3 typedef 关键字
五、转移表
一、字符指针变量
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
我们再来看下面这两段代码:
1. char arr[] = "abcdefg"
char*p = arr;
2. char* p = "abcdefg";
发现这其实不就是一个意思吗,答案是不一样的,
(1)代码1中,字符数组char arr[]中的内容(a b c d e f g /0)是可以修改的;
(2)而代码2中,”abcdefg“是常量字符串,这里的赋值是将字符串中首字符的地址赋值给p,常量字符串(a b c d e f g /0)不能被修改。
#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"); 1
else
printf("str1 and str2 are not same\n"); 2
if(str3 ==str4)
printf("str3 and str4 are same\n"); 3
else
printf("str3 and str4 are not same\n"); 4
return 0;
}
当我们执行后的结果会是语句2和语句3:
二、数组指针变量
2.1 数组指针是什么
- 指针数组 - 是数组,存放的是指针(地址);
- 数组指针 - 是数组还是指针?
- 字符指针 - char* - 指向字符的指针 - 字符指针变量中存放字符变量的地址:
- 整型指针 - int* - 指向整型的指针 - 整型指针变量中存放整型变量的地址:
- 数组指针 - 指向数组的指针 - 数组指针变量中存放数组的地址(&数组名);
下面代码哪一个是数组指针变量,p1 p2分别是什么:
2.2 数组指针变量初始化
int main()
{
int arr[10] = {0};
int (*p) [10] = &arr;//p就是数组指针,p中存放的是数组的地址
return 0;
}
通过调试发现&arr 和 p 的类型是完全一致的。
这里在回顾补充一些知识:
arr -- int* arr+1 跳过4个字节
&arr[0] -- int* &arr[0]+1跳过4个字节
&arr -- int(*)[10] &arr+1 跳过40个字节
三、二维数组传参的本质
void Pirnt(int arr[3][5], int r, int c)
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < 5; 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} };
Pirnt(arr, 3, 5);
return 0;
}
- 一维数组传参:
void Pirnt(int (*arr)[5], int r, int c)
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", *(*(arr + i) + j));//*(arr + i) == arr[i]
//arr[i]是第i行的数组名,数组名又表示数组首元素的地址,arr[i]表示是&arr[i][0]
}
printf("\n");
}
}
(二维数组传参,形参的部分可以写成数组,也可以写成指针形式)
四、函数指针变量
4.1 什么是函数指针变量
我们知道,变量有地址,数组也有地址,那么函数是否有地址呢?
我们来测试一下:
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int* pa = &a;//整型指针变量
int arr[5] = {0};
int (*parr)[5] = &arr;//parr 是数组指针变量
//arr &arr
//&函数名和函数名都是函数的地址,没有区别
printf("%p\n", &Add);
printf("%p\n", Add);
return 0;
}
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pf)(int,int) = &Add;//pf 函数指针变量
int (* pf)(int, int) = Add;//pf 函数指针变量
int ret2 = (*pf)(4, 5);
printf("%d\n", ret2);
int ret = Add(4, 5);
printf("%d\n", ret);
int (*pf)(int x, int y) = &Add;//pf 函数指针变量
//int (*)(int,int) 函数指针类型
return 0;
}
4.2 函数指针变量的使用
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pf)(int, int) = Add;
printf("%d", pf(3, 5));
return 0;
}
下面我们来看两端有趣的代码
- (1) ( *(void (*)() )0 ) ();
-
(2) void (*signal( int , void (*)( int )))( int );
//函数定义:
int Add(int x, int y)
{
return x + y;
}
//函数调用:
Add(int, int);
//函数定义:
int Add(int, int);
所以:上面代码(2)是一次函数声明,函数的名字叫:signal ,signal 函数的参数有两个,第一个参数是的类型是 int ,第二个参数的类型是一种函数指针 void(*)(int),该函数指针指向的函数参数是 int,返回类型是 void;signal函数的返回类型也是一个函数指针,类型是 void(*)(int)
,该函数指针指向的函数参数是 int ,返回类型是 void。
4.3 typedef 关键字
typedef unsigned int uint;
//将unsigned int 重命名为uint
(2)指针类型,能否重命名呢?其实也是可以的,⽐如,将 int* 重命名为 ptr_t ,这样写:
typedef int* ptr_t;
(3)数组指针和函数指针稍微有点区别: ⽐如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:
typedef int(*parr_t)[5]; //新的类型名必须在*的右边
(4)函数指针类型的重命名也是⼀样的,⽐如,将 void(*)(int) 类型重命名为 pf_t ,就可以这样写:
typedef void(*pfun_t)(int);//新的类型名必须在*的右边
所以,我们来看刚刚那两段有趣的代码,可以将 void(*)(int) 类型重命名为pf-t:
typedef void(*pf_t)(int);//新的类型名必须在*的右边
//简化两段代码:
typedef void(*pf_t)(int);
pf_t signal(int, pf_t);
五、转移表
在了解转移表之前,首先要了解一个新的名词:函数指针数组;
把函数的地址存到一个数组中,那这个数组就叫函数指针数组:
- int (*parr1[n])();
那函数指针数组有什么用呢,接下来我们来实现一个计算器的的例子说明:
(1)首先定义一些 加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)分别将函数的地址放进pf指针中,并且用函数指针数组存放这些函数的地址
int (*pf1)(int, int) = Add;
int (*pf2)(int, int) = Sub;
int (*pf3)(int, int) = Mul;
int (*pf4)(int, int) = Div;
int (* pf[4])(int, int) = {Add, Sub, Mul, Div};
// 0 1 2 3
(3)接下来我们把结果打印出来测试一下
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 (* pf[4])(int, int) = {Add, Sub, Mul, Div};
// 0 1 2 3
int i = 0;
for (i = 0; i < 4; i++)
{
int ret = pf[i](8, 4);
printf("%d\n", ret);
}
return 0;
}
(4.1)接下来就实现一个完整的计算器
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;
}
void menu()
{
printf("********************************\n");
printf("****** 1. add 2. sub *****\n");
printf("****** 3. mul 4. div *****\n");
printf("****** 0. exit *****\n");
printf("********************************\n");
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入两个整数:\n");
scanf("%d %d", &x, &y);
ret = Add(x, y);
printf("%d\n", ret);
break;
case 2:
printf("请输入两个整数:\n");
scanf("%d %d", &x, &y);
ret = Sub(x, y);
printf("%d\n", ret);
break;
case 3:
printf("请输入两个整数:\n");
scanf("%d %d", &x, &y);
ret = Mul(x, y);
printf("%d\n", ret);
break;
case 4:
printf("请输入两个整数:\n");
scanf("%d %d", &x, &y);
ret = Div(x, y);
printf("%d\n", ret);
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
}
}while (input);
return 0;
}
(4.2)使用函数指针数组 - 转移表
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
int (*pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div };
do
{
menu();
printf("请选择:\n");
scanf("%d", &input);
if (input > 0 && input < 5)
{
printf("请输入两位操作数:\n");
scanf("%d %d", &x, &y);
ret = pfArr[input](x, y);
printf("%d", ret);
break;
}
else if (input == 0)
{
printf("游戏结束,退出游戏\n");
break;
}
else
{
printf("选择错误,请重新选择\n");
}
} while (input);
return 0;
}
未完待续~~