目录
一、字符指针
字符指针:指向字符的指针
1.1 ❥ 使用场景
【使用场景一】
#include <stdio.h>
int main()
{
char c = 'w';
char* pc = &c;
*pc = 'x';
printf("%c\n", *pc);
return 0;
}
【使用场景二】
#include <stdio.h>
int main()
{
char* p = "abcdef";//这里把首字符a的地址赋给了变量p
printf("%s\n", p);
return 0;
}
注意
易错点1:
原因:
易错点2:
不能解引用p,解引用打印的是一个字符,一个字符不能用%s打印。
char* p = "abcdef";
printf("%s", *p);//error
以下是将字符串放在数组里面:
char arr[] = "abcdef";
而数组的内容是可变的。
1.2 ❥ 有关字符串笔试题
#include <stdio.h>
int main()
{
char arr1[] = "hello";
char arr2[] = "hello";
const char* p1 = "hello";
const char* p2 = "hello";
if (arr1 == arr2)
printf("arr1=arr2\n");
else
printf("arr1!=arr2\n");
if (p1 == p2)
printf("p1=p2\n");
else
printf("p1!=p2\n");
return 0;
}
运行结果如下:
原因如下:
二、数组指针
整型指针:存放整型变量的地址,能够指向整型数据的指针
浮点型指针:存放浮点型变量的地址,能够指向浮点型数据的指针
那数组指针:存放数组的地址,指向数组的指针
2.1 ❥ 数组指针变量
看下面两行代码,p1,p2分别是什么?
int* p1[10];
int(*p2)[10];
- p1是指针数组
p1是数组名,该数组里存放了10个元素,每个元素是int*类型
- p2是数组指针
因为p2先和*结合,说明p是一个指针变量,然后指向一个大小为10个整型的数组,所以p是一个指针,指向一个数组,所以叫数组指针。
注意:
2.2 ❥ 数组指针类型
去掉指针变量名就是指针(变量)的类型。
看如下代码:
它们跳过的字节不同就是因为他们的类型不同导致。
注意:
2.3 ❥ 数组指针的初始化
数组指针存放的是数组的地址。
所以初始化的时候要给整个数组的地址。代码如下:
注意:
三、数组指针的使用
3.1 ❥ 二维数组和数组名的理解
首先我们来理解一下二维数组及其数组名:
在c语言中,只有一维数组(N维数组的元素是数组),数组名作为指针时永远指向第一个元素。
如:
- 数组a[3]; *a=a[0]
- 数组a[3][4]; *a=a[0] 只不过这时候a[0]又是一个数组。
这时候的a[0]又是指向它自己元素的第一个元素,又有 *a[0]=a[0][0]
- 这种方式可以推广到N维数组,所有数组直接对数组名取地址(如:&a),得到的指针指向该数组,而不是指向第一个元素。注意这点区别。
board:一维数组的地址。
二维数组的数组名,数组名就是首元素地址。我们知道,可以把一维数组看作二维数组的元素。所以,board就是一维数组的地址。
&board:取出的是整个二维数组的地址。
board[0]:第一行第一个元素的地址。
解引用,相当于拿到第一行数组的数组名,也就是首元素地址,即第一行第一个元素的地址。
board[0]=*board=&board[0][0]
&board[0]:第一行的地址。
board=&board[0]
3.2 ❥ 二维数组传参
清楚了上面的概念之后,我们来看下面一段代码:
之前二维数组传参时,形参部分用的是数组接收。
#include <stdio.h>
void test(int a[3][5], int r, int c)
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
printf("%d ", a[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;
}
那么实参就可以写成数组指针的形式,代码如下:
#include <stdio.h>
void test(int(*p)[5], int r, int c)//数组指针
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
printf("%d ", *(*(p + 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;
}
问题:为什么*(p+i) 跳过的是一行数组?
回答:
四、函数指针
函数指针:存放函数的地址,指向函数的指针
4.1 ❥ 函数的地址
函数是否有地址呢?我们来测试一下:
由测试结果可知:函数存在地址,取&函数名和函数名拿到的都是函数的地址。
4.2 ❥ 函数指针变量
我们通过函数指针来存储函数的地址。
int (*pf) (int x, int y) = &Add;
int (*pf) (int , int ) = Add;//xy可以省略,只写类型
- 地址要存起来,放到(指针)变量里去。
- pf是变量名,*pf说明是指针,指向的是函数,所以加上括号()。
- 函数的参数是int (参数名写不写无所谓,只要类型交代清楚即可),函数的返回值类型也是int。
int (*pf) (int x, int y)
| | ------------
| | |
| | pf指向函数的参数类型和个数的交代
| 函数指针变量名
pf指向函数的返回类型
int (*) (int x, int y) //pf函数指针变量的类型
4.3 ❥ 函数指针变量的使用
通过函数指针调用指针指向的函数。
代码如下:
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf)(int, int) = Add;
printf("%d\n", (*pf)(2, 3));//输出结果为5
printf("%d\n", pf(2, 3));//输出结果为5
return 0;
}
把函数的地址存到pf里,通过解引用pf找到函数,找到这个函数要调用这个函数,调用函数需要传参,所以();(传参),传2,3。这样的话它会把2和3相加,得到5。
应用:通过函数指针的方式进行调用
#include <stdio.h>
int Add(x, y)
{
return x + y;
}
void cale(int(*pf)(int,int))
{
int a = 3;
int b = 5;
int ret = pf(a, b);
printf("%d\n", ret);
}
int main()
{
cale(Add);
return 0;
}
五、函数指针数组
函数指针数组:把一个函数的地址存放到一个数组中。
是个函数指针类型的数组。
去掉 函数名+[ ] 就是该数组的类型。
解释:parr先和[ ]结合,说明parr是数组,数组的内容是int(*)()类型的函数指针。
当对函数指针数组进行初始化的时候,后面初始化的可以省略掉数组的大小,它会根据后面初始化的内容来确定数组的大小。
例如:
int(*p[5])(int x, int y) = { 0, add, sub, mul, div };
int(*p[])(int x, int y) = { 0, add, sub, mul, div };
六、转移表
使用了函数指针数组,避免大篇幅地修改内容;也可实现跳转的功能。
所以函数指针数组也叫:转移表。
计算机的一般实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
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;
}
使用函数指针数组的实现
#include <stdio.h>
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;
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;
}