数组指针
1、理解两种概念:
数组指针与指针数组
int arr[5];//数组
int *p[5];//指针数组
int (*p)[5];//数组指针
int (*p[10])[5];//存放指针数组的数组
数组指针(也称行指针):定义 int (*p)[n];
- ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
- 如要将二维数组赋给一指针,应这样赋值:
指针+1,加上其所指向类型的大小
int a[3][4] ={ {1,2,3,4},{4,5,6,7},{8,9,0,1} };
int(*pi)[4] = a;
int arr[4] = {1,2,0,8 };
int(*p)[4] = arr;
printf("%p\n", pi); //0115FBDC
printf("%p\n", pi+1);//0115FBEC 二维指针发生降维,指针是指向一维数组的指针;
printf("%d\n", **(pi + 1));//4
printf("%p\n", p);//0115FBB8
printf("%p\n", p+1);//0115FBC8 一维数组的数组指针;
printf("%d\n",*(*(p+1)-1));//8
指针数组:定义 int *p[n];
[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。
执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它们分别是指针变量可以用来存放变量地址。
*p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
将二维数组赋给一指针数组:
int b[3][4] = { {1},{2},{3} };
int *p1[3];
int i = 0;
for (; i < 3; i++) {
p1[i] = b[i];
printf("%d ", *p1[i]);//1 2 3
printf("\n");
printf("%p ", p1[i]);//0136F958(&b) 0136F968 0136F978
printf("\n");
}
2、储存数组地址
int arr[10] = {1,2, 0 };
int(*p)[10] = &arr;//数组指针
printf("%p\n", p); //001AFBEC(指向数组地址的指针)
printf("%p\n", &arr);//001AFBEC(数组的地址)
printf("%p\n", *p); //001AFBEC(解引用之后的内容为&arr)
printf("%d\n", **p); //1(数组第一个元素的内容)
3、指针与数组的定义与声明
- 定义:(开辟空间)让不存在的存在;
- 声明:(不开辟空间)让不知道的知道;
- 声明其实就是定义的变量本身,因此,声明与定义必须保持一致。
数组参数与指针参数
1、一维数组传参:
int a[10] = {0}; //int a int a[10] int *a
int *b[20] = { 0 };// int *b int *b[10] int **b
2、二维数组传参:
int a[1][5] = { 0 };//int a[][5] int a[1][5] int (*a)[5]
3、一级指针传参:
参数为一级指针的情况:一维数组(类型区别),一维指针,
int main(){
int a[5]={2,3,4,5,1};
int *p=a;
print(p,2);//(int *p,int n)
}
4、二级指针传参:
参数为二级指针的情况:二级指针,指针数组,一级指针地址
int n=10;
int *p=&n;
int **pp=&p;
test(pp);// int **ppa
函数指针
1、函数指针就是函数的指针;(入口地址)
定义:void (*pfun)()
char * (*fun1)(char * p1,char * p2);
//fun1不是什么函数名,而是一个指针变量,它指向一个函数。这个函数有两个指针类型的参数,函数的返回值也是一个指针。
//(改写之后为 char * (*)(char * p1,char * p2) fun1;)
char * *fun2(char * p1,char * p2);
//函数的返回值类型为char**,是个二级指针。
char * fun3(char * p1,char * p2);
//fun3是函数名,p1,p2是参数,其类型为char *型,函数的返回值为char *类型。
- 在Visual C++6.0里,给函数指针赋值时,可以用&fun或直接用函数名fun。这是因为函数名被编译之后其实就是一个地址,所以这里两种用法没有本质的差别。
两段有趣的代码
1、
(*(void(*)())0()//将0强转成返回值为void的函数指针,并且调用这个指针指向的函数。
- float (*h)()//h是返回值为float的函数指针。 - (float(*)())//类型强转符
- (*p)()//若p为函数指针,调用p所指向的函数 2、 void (*signal(int ,void(*)(int)))(int)
//返回值为void(*)(int)的函数指针
2、函数指针数组、函数指针的数组的指针
-
函数指针数组: 存放函数地址的数组;
- int (*p[10]) ()
- 函数指针的用途:转移表;
函数指针数组的指针:指向指针数组元素都是函数指针的指针
回调函数
一个通过函数指针调用的函数。其不是由该函数的实现方法直接调用,而是通过特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件的响应。
实例:模拟实现qsort(冒泡法)链接如下:(https://blog.csdn.net/fayfayfaydyt/article/details/80465345))
计算器实例
#pragma warning (disable:4996)
int add(int a,int b) {
return a + b;
}
int mul(int a, int b) {
return a * b;
}
int sub(int a, int b) {
return a - b;
}
int dv(int a, int b) {
if (b == 0) {
printf("div zero!error!\n");
return 0; //分母为0;
}
return a / b;
}
void memu( ) {
printf("******************************\n");
printf("***** 1、add 3、sub *****\n");
printf("***** 2、mul 4、div *****\n");
printf("***** 0、quit *****\n");
printf("******************************\n");
}
int main()
{
int x, y;
int input = 0;
int ret = 0;
int (*p[5])(int ,int )= { 0,add,sub,mul,dv };//函数指针数组,p[0],p[1],p[2]...p[5];
do {
memu();
printf("请选择\n");
scanf("%d", &input);
if ((input <= 4 && input>0))//判断输入是否满足循环
{
printf("输入操作数:");
scanf("%d%d", &x, &y);
ret = p[input](x, y);//调用函数p[input]
printf("%d\n", ret);
}
else if (input==0)
{
break;
}
else {
printf("输入有误\n");
}
} while (1);
system("pause");
return 0;
}
综上,指针就是地址,地址的地址叫二级指针,地址的数组叫指针数组,指向数组的指针叫数组指针,指向函数的指针叫函数指针。