(一)字符指针变量
字符指针的一般使用,和之前指针变量存储整型变量使用方法一样
int main()
{
char ch = 'a';
char* pa = &ch;
//*pa='a';
return 0;
}
还有一种使用方法,如下所示
int main()
{
char ch = "abcdef";
const char* pa = "abcdef";
return 0;
}
这里并不是把字符串abcdef存入到字符指针pa中,而是将字符串首字母的地址存放pa中 这里用const*pa来存储常量字符串的首元素地址,const表示不希望修改字符串的内容
观察以下代码,更深入的了解字符数组和字符指针的区别
int main()
{
char s1[] = "abcdef";
char s2 [] = "abcdef";
const char* s3 = "abcdef";
const char* s4 = "abcdef";
if (s1 == s2)
{
printf("s1==s2\n");
}
else
{
printf("s1!=s2\n");
}
if (s3 == s4)
{
printf("s3==s4\n");
}
else
{
printf("s3!=s4\n");
}
return 0;
}
这里的s1和s2虽然用相同的常量字符串去初始化它们,但是它们会开辟出不同的内存块去存储,但是s3和s4作为字符指针,它们指向同一个字符串的时候,实际上会指向同一个内存,所以s1和s2不同,s3和s4相同
(二)数组指针变量
1.数组指针变量的定义
数组指针是指针变量,里面存放的是数组的地址,是能够指向数组的指针变量,一般定义为如下所示
int main()
{
char arr[6];
char *pc[6]=&arr; //指针数组
char (*pa)[6] = &arr;//数组指针
return 0;
}
注意⚠️:*pa要用()包含,因为[]的优先级高于*,如果不用()将*pa包含在内,那么就会变成指针数组,表示存放数组的指针
2.数组指针变量的初始化
int main()
{
int arr[10] = { 0 };
int (*p3)[10] = &arr;//int(*)[10] 数组指针类型
return 0;
}
之前我们用指针数组将数组中每个元素都访问了一遍,并且能够正确的在屏幕上打印出来,那么我们用数组指针也能访问数组的每个元素呢,答案是可以的,只不过和指针数组不同,在打印数组中的每个元素时不能使用p++,因为这里的p+1代表的不是跳过一个字节,而是跳过一整个数组的字节,所以我们可以使用(*p)[i]来逐个打印,这里的*p代表*&arr,也就是代表arr
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int(*p)[10] = &arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ",(*p)[i]);
}
return 0;
}
那么可能会有人想,这么做要比之前用指针数组做的时候要麻烦,那是因为数组指针并不是用在这个地方的,而是接下来的二维数组传参中的
3.二维数组传参的本质
我们之前学习了一维数组传参的本质,它是讲数组首元素的地址传递给函数,而二维数组其实也是将二维数组首元素的地址传递给函数形参部分,只不过二维数组中的元素是一维数组,所以二维数组首元素的地址就是一维数组那一行的地址,而这时候我们可以用数组指针接受第一行数组的地址,接着总之前所学的知识将二维数组的每个元素打印出来
void print(int(*arr)[5], int i, int j)
{
int a = 0;
int b = 0;
for (a = 0; a < i; a++)
{
for (b = 0; b < j; b++)
{
printf("%d ", *(*(arr + a) + b));
}
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;
}
注意⚠️:在函数部分用数组指针接受的首行数组元素的地址,要用int(*p)[5],表示指向一行5个int,而不是用int(*p)[3][5],int(*p)[3][5]表示的是指向整个3×5二维数组的指针,而这里我们只需接受首行5个int类型的变量的地址
这里的*(arr+a)表示的是第几行,a=0时,表示二维数组的第一行,以此类推,然后在后面加上b表示从第0列开始往后依次访问,最终表达式就为*(*(arr+a)+b)
(三)函数指针变量
1.函数指针变量的创建
函数指针变量就是存放函数的地址,那么函数有没有地址呢,我们可以试一下
观察发现,函数也是有地址的,所以我们用函数指针变量来存放函数的地址 函数指针变量的创建格式一般如下
int Add(int x, int y)
{
return x + y;
}
int add(int a, char ch)
{
}
int main()
{
int(*pf)(int,int) = &Add;//pf就是函数指针变量
int(*pa)(int, char) = &add;//pa也是函数指针变量
return 0;
}
2.函数指针变量的使用
我们可以通过解引用操作符*来获取函数指针变量所指向的函数,在这里注意一点,函数名也是函数的地址,所以&Add和Add其实是一样的
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf)(int,int) = &Add;//pf就是函数指针变量
int re = (*pf)(3, 5);
printf("%d", re);
return 0;
}
这里的(*pf)指向了Add函数,其实pf=&Add=Add,所以这里不用解引用操作符*,直接用pf也可以直接指向函数Add()
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf)(int,int) = &Add;//pf就是函数指针变量
int re = pf(3, 5);//pf=&Add=Add
printf("%d", re);
return 0;
}
依旧能得出结果
3.typedef
(1)普通变量类型
typedef是用来类型重命名的,比如说我们定义一个unsigned int类型的变量,我们觉得这个单词太长了,以后写起来不方便,我们就可以用typedef来重新命名,如下所示
int main()
{
typedef unsigned int uint;
unsigned int b;
uint a;
return 0;
}
调试我们发现a,b变量均为unsigned int类型

(2)指针类型
我们可以用typedef重新命名指针变量,如下所示
int main()
{
typedef int* pf;
int* pa;
pf pb;
return 0;
}
我们可以看出pa和pb的类型都是int*类型
typedef还有一个方便的地方,比如说我们想要定义多个int*类型的变量,使用使用如下代码,
int main()
{
int* pa, pb, pc;
return 0;
}
但是调试可以发现,只有第一个变量是int*类型,其他的是int类型,
这时候我们就可以使用typedef来重命名int*,如下所示
int main()
{
typedef int* pf;
pf pa, pb, pc;
return 0;
}
调试我们发现所有变量均为int*类型
(3)数组指针和函数指针
它们和上面两个的命名有点不同,它们的命名格式如下
int main()
{
typedef int(*pf)[5];//数组指针类型
int(*arr1)[5];
pf arr2;//pf=int (*)[5]
int (*pa)();
typedef int (*pc)();//函数指针类型
pc pv;//pc=int (*)()
return 0;
}
(四)函数指针数组
我们把函数的地址存放到一个数组中去叫作函数指针数组,定义如下
int main()
{
int (*parr[5])();//parr先和[]结合,说明parr是数组,
//而数组的内容是int(*)()类型的函数指针
return 0;
}
那么具体该如何去使用它呢,我们通过下面一个题目深入了解
1.转移表
计算机的实现
我们完成一个简单计算机的代码实现,要求用户输入两个数字,可以进行简单的加减乘除运算,我们先不用函数指针数组来完成,通过之前的扫雷和猜数字游戏,相信我们可以写出这段代码,如下所示
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int mlt(int x, int y)
{
return x * y;
}
int div(int x, int y)
{
return x / y;
}
void menu()
{
printf(".....................\n");
printf("......1.Add.........\n");
printf("......2.Sub.........\n");
printf("......3.mlt..........\n");
printf("......4.div..........\n");
printf("......0.over..........\n");
}
int main()
{
int a = 0;
int b = 0;
int input = 0;
int ret = 0;
do
{
menu();
printf("请选择:\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入两个操作数:\n");
scanf("%d %d", &a, &b);
ret = Add(a,b);
printf("%d", ret);
break;
case 2:
printf("请输入两个操作数:\n");
scanf("%d %d", &a, &b);
ret = Sub(a,b);
printf("%d", ret);
break;
case 3:
printf("请输入两个操作数:\n");
scanf("%d %d", &a, &b);
ret = mlt(a,b);
printf("%d", ret);
break;
case 4:
printf("请输入两个操作数:\n");
scanf("%d %d", &a, &b);
ret = div(a,b);
printf("%d", ret);
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误\n");
break;
}
} while (input);
return 0;
}
我们可以发现,在case语句中,有太多冗长的相同的语句,为了使代码更加简洁,我们可以使用函数指针数组,将4个运算函数的地址存入其中
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int mlt(int x, int y)
{
return x * y;
}
int div(int x, int y)
{
return x / y;
}
void menu()
{
printf(".....................\n");
printf("......1.Add.........\n");
printf("......2.Sub.........\n");
printf("......3.mlt..........\n");
printf("......4.div..........\n");
printf("......0.over..........\n");
}
int main()
{
int a = 0;
int b = 0;
int input = 0;
int ret = 0;
int(*parr[5])(int, int) = { 0,Add,Sub,mlt,div };
do //数组首元素的下标是0,所以加一个数字,
{ //这样加减乘除的函数下标就分别是1,2,3,4
menu();
printf("请选择:\n");
scanf("%d", &input);
if (input <= 4 && input >= 1)
{
printf("请输入两个操作数:");
scanf("%d %d", &a, &b);
ret = (*parr[input])(a, b);
printf("%d\n", ret);
}
else if (input == 0)
{
printf("退出游戏\n");
}
else
{
printf("输入错误\n");
}
} while (input);
return 0;
}

950

被折叠的 条评论
为什么被折叠?



