目录
指针概念
- 指针是一个变量,用来存放地址,地址唯一标识一块内存地址
- 指针的大小是固定的4/8个字节(32位平台/64位平台)
- 指针式有类型的,指针的类型决定了指针的解引用后所能访问的内存空间以及决定指针的步径。
- 指针的运算
字符指针
在指针的类型中我们知道有一种指针类型为字符指针 char*
char ch = 'w';
char *p = &ch;
下面有一个经常会出现的面试题:
int main()
{
char str1[] = "hello bit";
char str2[] = "hello bit";
char *str3 = "hello bit";
char *str4 = "hello bit";
if (str1 == str2)
{
printf("str1 == str2\n");
}
else
{
printf("str1 != str2\n");
}
if (str3 == str4)
{
printf("str3 == str4\n");
}
else
{
printf("str3 != str4\n");
}
system("pause");
return 0;
}
出现的结果
在这个代码中,str3和str4指向的是同一个常量字符串,C会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际都是指向了一块内存,但是用相同的常量字符串去初始化不同的数组时就会开辟不同的内存块,所以str1和str2不同,str3和str4相同。画个图表示一下:
指针数组
指针数组就是存放指针的数组
int arr1[3] = { 1, 2, 3 };
int arr2[3] = { 4, 5, 6 };
int arr3[3] = { 7, 8, 9 };
int* p[3] = { arr1, arr2, arr3 };
上边的代码用图表示如下:
数组指针
指向数组的指针
int (*p)[3]
上边这句代码就是一个数组指针,p先和*结合,说明p是一个指针变量,然后指向的是一个大小为3个int型的数组,所以p是一个指针,指向一个数组,叫做数组指针。
&数组名VS数组名
int main()
{
int arr[3] = { 1, 2, 3 };
int(*p)[3] = &arr;
printf("arr = %d\n", arr);
printf("&arr = %d\n", &arr);
printf("arr+1 = %d\n", arr+1);
printf("&arr+1= %d\n", &arr+1);
system("pause");
return 0;
}
结果显示:
根据结果显示发现,其实arr与&arr,虽然值一阿姨那个,但是意义是不一样的。
实际上&arr表示的是数组的地址,并不是数组首元素的地址,而arr表示的是数组首元素的地址。
数组的地址+1,即跳过整个数组的大小,所以&arr+1相较于&arr的差值为12。
在学习了指针数组和数组指针,可以看看下边这个是什么意思:
int (*p[4])[5]
首先p是一个数组,数组的大小为4,数组里边放的是数组指针,指针指向的数字大小为5,里边放的是int型的数据。用图表示如下:
函数指针
void (*p)();
上边这句代码就是写了一个函数指针,p先和*结合,说明p是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void
下边写一个函数指针应用代码
int Max(int a, int b)
{
return a > b ? a : b;
}
int Fun(int a, int(*p)(int, int))
{
int max = p(10,20);
return a > max ? a : max;
}
int main()
{
int a = Fun(100, Max);
printf("%d\n", a);
system("pause");
return 0;
}
在上边的代码中Max = int(*p)(int, int)
函数指针数组
int (*p[10])()
上边这句代码就是写了一个函数指针数组,p 先和 [10] 结合,说明p是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针。
函数指针数组用途:转移表
下边我们来实现一个简易的计算器
未使用转移表
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.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;
}
void menu()
{
printf("请选择正确的操作\n");
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf("*************************\n");
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入两个操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("结果是:%d\n", ret);
break;
case 2:
printf("请输入两个操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("结果是:%d\n", ret);
break;
case 3:
printf("请输入两个操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("结果是:%d\n", ret);
break;
case 4:
printf("请输入两个操作数:");
scanf("%d %d", &x, &y);
ret = Div(x, y);
printf("结果是:%d\n", ret);
break;
default:
printf("输入错误,重新输入\n");
break;
}
} while (input);
system("pasue");
return 0;
}
我们可以看到,这段代码是非常冗余的,有很多重复的代码,下边我们使用转移表来简化该代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.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;
}
void menu()
{
printf("请选择正确的操作\n");
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf("*************************\n");
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
do
{
menu();
scanf("%d", &input);
if ((input <= 4 && input >= 1))
{
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = (*p[input])(x, y);
}
else
{
printf("输入有误\n");
}
printf("ret = %d\n", ret);
} while (input);
system("pasue");
return 0;
}
相较于上边的代码,下边的代码看起来就会好很多
int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
这句代码就是函数指针数组,p是一个数组,数组的内容是int(*)(int,int)类型的函数指针。
函数指针数组的指针
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针;
#include <stdio.h>
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函数指针pfun
void(*pfun)(const char*) = test;
//函数指针的数组pfunArr
void(*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void(*(*ppfunArr)[10])(const char*) = &pfunArr;
system("pause");
return 0;
}
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。