指针与数组
先来看一个很普通的数组:
int arr[10]={.....};
它里面存的是10个整形数据,即 “int int int int......”
指针数组与它类似:顾名思义,就是用来存指针的数组。
int* arr[10]={arr1,arr2,arr3.....}
它里面存的是10个地址(数组名作首元素地址),即“int* int* int* int*......”。每个“int*”都代表一个地址。
现在来复习一些之前的知识:
数组名代表数组首元素地址,可以把它当成一个指针,加1就会跳过4个字节,所以前4个输出很容易理解。关键是最后2个输出,对数组名取地址,实际上取出的是整个数组的地址,如果让它加1,就会向后跳过40个字节。
那有没有这样一种指针,它指向的是整个数组,而非首元素地址?
实际上是有的,它叫数组指针,如果让这个指针加1,则会向后跳过一个数组的大小。
数组指针
如果我想把整个数组的地址存起来,就要用到数组指针了。
int arr[10]={0};
int (*p)[10]=&arr;
对于“int (*p)[10]=&arr”,p前面有个 * 代表它是一个指针变量,它后面的[10]代表它指向一个大小为10个元素的数组,前面的int代表数组的类型。
那如果我想把整个指针数组的地址存起来呢?可以这样写:
int* arr[10]={arr1,arr2,arr3....};
int* (*p)[10]=&arr;
p前面的 * 代表它是一个指针变量,它后面的[10]代表它指向一个大小为10个元素的数组,前面的int*代表数组元素的类型。
存放数组指针的数组
把数组指针稍微改变一下:
int (*parr[5])[10]={&arr1,&arr2,&arr3.....};
parr是数组名,右边的5代表这个数组有5个元素,其余的是这个数组的类型(int (*) [10]);
里面每一个元素类型都是int (*) [10],即每一个元素都是数组指针,指针指向的数组有10个元素,类型为int 。
有关指针的实参和形参
1.我定义int arr[10]={0};
如果现在arr作为实参传到函数里,那形参可以是(int arr[]) or (int* arr)
2.我定义int* arr[10]={0};
如果现在arr作为实参传到函数里,那形参可以是(int* arr[]) or (int** arr)
这里解释一下形参可以用int** arr的原因:int* arr[10]里面存的是{int* int* int* int*......},存的本来就是一级指针,一级指针的地址应该用二级指针变量来接受。
3.我定义int arr[3][5]={.......};
如果现在arr作为实参传到函数里,那形参可以是什么呢?
arr是一个二维数组,二维数组的数组名代表第一行元素的地址,而二维数组的每一行又可以看作一个一维数组,所以二维数组的数组名就等价于第一行的一维数组的地址,“一维数组的地址”并非首元素地址,应该用数组指针来接收。
所以形参可以写(int arr[][5]) or (int arr[3][5]) or (int (*p)[5])
4.以下面的代码为前提:
int arr[10]={.....};
int a=10;
int* p=&a;
如果(int*p)作形参,则实参可以是(arr) or (p) or (&a)。
int** p2=&p;
int* arr2[10]={.....};
如果(int** p)作形参,则实参可以是(p2) or (&p) or (arr2) 。
函数指针
实际上,函数也是有地址的。看下面的代码:
int ADD(int x,int y)
{
return x+y;
}
int main()
{
int (*pf)(int,int)=&ADD;
}
我定义了一个ADD函数,指针pf可以接收它的地址:pf前面的 * 代表它是指针,后面的()代表它指向一个函数,()里面写函数的参数类型,最前面的int是函数的返回类型。
此时pf就是一个函数指针。
值得注意的一点就是:&函数名和函数名都代表函数的地址,&ADD也可以写成ADD。
那么该如何使用呢?
int ret =(*pf)(2,3);
和函数的用法类似,其实也可以写成int ret =pf(2,3);
函数指针-计算器
那么函数指针有什么用呢?
下面用一个简单的(全是整形运算)计算器来说明:
#include<stdio.h>
void menu()
{
printf("************************************\n");
printf("****** 1.加法 2.减法 ******\n");
printf("****** 3.乘法 4.除法 ******\n");
printf("****** 0.退出 ******\n");
printf("************************************\n");
}
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 Div(int x, int y)
{
return x / y;
}
int clue(int(*pf)(int,int))
{
int a = 0, b = 0;
printf("请输入两个数:");
scanf("%d %d", &a, &b);
int ret = (*pf)(a, b);
printf("%d\n", ret);
}
int main()
{
int input = 0, ret = 0;
while (1)
{
menu();
printf("请选择:");
scanf("%d", &input);
switch(input)
{
case 1:clue(Add);break;
case 2:clue(Sub);break;
case 3:clue(Mul);break;
case 4:clue(Div);break;
case 0:return 0;
default:printf("请重新选择:");
}
}
return 0;
}
值得注意的就是clue函数:
int clue(int(*pf)(int,int))
{
int a = 0, b = 0;
printf("请输入两个数:");
scanf("%d %d", &a, &b);
int ret = (*pf)(a, b);
printf("%d\n", ret);
}
它专门用来接收不同函数的地址,从而通过指针进行不同的运算,但前提是他们的返回类型和参数类型应该相同。
如果没有函数指针,恐怕每个case里面都要写下面这一串代码了:
int a = 0, b = 0;
printf("请输入两个数:");
scanf("%d %d", &a, &b);
int ret = 函数名(a, b);
printf("%d\n", ret);