目录
[1. 字符指针变量](#1. 字符指针变量)
[2. 数组指针变量](#2. 数组指针变量)
[3. ⼆维数组传参的本质](#3. ⼆维数组传参的本质)
[4. 函数指针变量](#4. 函数指针变量)
[5.typedef 关键字](#5.typedef 关键字)
[6 函数指针数组](#6 函数指针数组)
1. 字符指针变量
在指针的类型中我们知道有⼀种指针类型为字符指针 char*
#include <stdio.h>
int main()
{
char* ch = "asdfff ";//这个表达式就是字符指针表达式
//这里的字符串是常量字符串,将a的地址赋值给ch
return 0;
}
常量字符串是不能被修改的 , 使用% s 打印字符串时候只需要首字符的地址即可
下面大家来看这个代码:可以先思考一下这个代码的运行结果是什么?
我们发现代码中的字符串不都是一样吗? 那一定就是都相同了。可结果是这样吗?
#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;
}
答案并不是:这是为什么呢?
这里因为str 1和str2 都是数组变量 ,当然虽然他们存的字符都相同,但是他们的地址不同,所以就有了str 1 不等与str2,数组是可以被修改的 。
那么str3 和ste4 为什么又相等呢?由代码中我们可以看到 str 3和str4 前面都有加const 来修饰变量,当const 修饰字符串就成为了常变量字符串,此时这里的常量字符串是不可被修改的,所以str3 和str 4 的地址是同一个地址。
2. 数组指针变量
指向数组的指针,存放数组的地址
int main()
{
int arr[5] = { 1,2,3,4,5 };
int (*p) [5] = &arr;//这里的p就是数组指针,p中存放的是数组的地址return 0;
}
解释:p先和*结合,说明p是⼀个指针变量,然后指针指向的是⼀个⼤⼩为10个整型的数组。所以p是 ⼀个指针,指向⼀个数组,叫 数组指针。 这⾥要注意:[]的优先级要⾼于*号的,所以必须加上()来保证p先和*结合。
3. ⼆维数组传参的本质
有了数组指针的理解,我们就能够讲⼀下⼆维数组传参的本质了。
过去我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们是这样写的:
void Print(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]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
Print(arr, 3, 5);
return 0;
⾸先我们再次理解⼀下⼆维数组,⼆维数组其实可以看做是每个元素是⼀维数组的数组,也就是⼆维 数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。
然而第一行就是二位数组的首地址。一维数组的首地址就是一个元素
那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏就是这个⼀维数组的地址。
代码优化:
void Print( 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));// *(p+i)等价于 *p[i]就是找到一行的第一个元素的地址 +j是因为j是列数,找到下标元素的内容
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { { 1,2,3,4,5} , {2, 3, 4, 5, 6}, {3, 4, 5, 6, 7}};
Print(arr, 3, 5);
return 0;
}
可以结合上面的图来理解:
画的有些潦草,不过大概就是这么个意思
总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式
4. 函数指针变量
那要把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢
void Add(int x, int y)
{return x + y;
}
int main()
{
int arr = 10;
int (*pa)(int, int) = &Add;//pa就是函数指针int ret = (*pa)(4, 5);
printf("%d ", ret);
return 0;
}
函数指针类型解析
int (*pf3) (int x, int y)
| | ------------
| | |
| | pf3指向函数的参数类型和个数的交代
| 函数指针变量名
pf3指向函数的返回类型
int (*) (int x, int y)
5.typedef 关键字
typedef 是⽤来类型重命名的,可以将复杂的类型,简单化
⽐如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤
typedef unsigned int uint;//将ungsigned int 简化为uint 名字自己来写
int main()
{
unsigned int n;uint n2;
return 0;
}
类型也可以 自定义名称,与类型相同
#include <stdio.h>
typedef int(*type)[5]; //当指针类型定义时候要把名称写在*号右面
int main()
{
int a = 10;
int(*p)[5] = &a;
type p2 = &a;//type就是指针类型
return 0;
}
6 函数指针数组
数组是⼀个存放相同类型数据的存储空间,我们已经学习了指针数组,
#include <stdio.h>
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 Dvi(int x, int y)
{
return x / y;
}
int main()
{
/*int (*p1)(int, int) = Add;
int (*p2)(int, int) = Sub;
int (*p3)(int, int) = Mul;
int (*p4)(int, int) = Dvi;*/
//此时上面的函数指针也可以优化为下面的函数指针数组 ,来存放这些地址
int (*p[4])(int, int) = { Add ,Sub,Mul,Dvi };//这里存储的类型必须相同,相同类型元素
int i = 0;
for (i = 0; i < 4; i++)
{
int ret = p[i](9, 3);//p[i]是上面函数指针数组
printf("%d\n", ret);
}
return 0;
}
7.转移表
函数指针数组的⽤途:转移表 就是函数指针数组
计算器的⼀般实现
计算器的实行代码。
#include <stdio.h>
int Sum(int x, int y)
{
printf("请输入两个操作数\n");
scanf("%d%d", &x, &y);
return x + y;
}
int Sub(int x, int y)
{
printf("请输入两个操作数\n");
scanf("%d%d", &x, &y);
return x - y;
}
int Mul(int x, int y)
{
printf("请输入两个操作数\n");
scanf("%d%d", &x, &y);
return x * y;
}
int Div(int x, int y)
{
printf("请输入两个操作数\n");
scanf("%d%d", &x, &y);
return x / y;
}
void mune()
{
printf("********************\n");
printf("*****1.sum 2.sub****\n");
printf("*****3.mul 4.div****\n");
printf("***** 0 . exit ***\n");
printf("********************\n");
}
int main()
{
int input = 0;
int a = 0;
int b = 0;
int ret = 0;
do
{
mune();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
ret =Sum(a,b);
printf("%d\n ", ret);
break;
case 2:
ret =Sub(a, b);
printf("%d\n ", ret);
break;
case 3:
ret =Mul(a, b);
printf("%d\n ", ret);
break;
case 4:
ret =Div(a, b);
printf("%d\n", ret);
break;
case 0:
printf("退出计算器\n");
break;
default :
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
但是如果发现你不仅仅需要这几个,老板让你增加几个字符操作,上面代码太麻烦了
可以使用函数指针数组来实现这个计算器。
#include<stdio.h>
int Sum(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 mune()
{
printf("********************\n");
printf("*****1.sum 2.sub****\n");
printf("*****3.mul 4.div****\n");
printf("***** 0 . exit ***\n");
printf("********************\n");
}
int main()
{
int input = 0;
int a = 0;
int b = 0;
int ret = 0;
int (*p[5])(int, int) = { 0,Sum,Sub,Mul,Div };//假设要加入>> 操作符 可以在后面添加,
//指针数量增加,添加0 是为了下标元素对应
do
{
mune();
printf("请选择:");
scanf("%d", &input);
if ( input >=1&&input <=4)
{
printf("请输入两个操作数\n");
scanf("%d%d", &a, &b);
ret = p[input](a, b);
printf("%d\n", ret);
}
else if (input == 0)
{
printf("退出计算器\n");
}
else
{
printf("输入错误,请重新输入\n");
}
} while (input);
return 0;
}
上面的代码已经得到了优化