[ ] 的优先级高于* ,所以在写数组指针的时候一定要加上( ),不然就是,指针数组的意思了
一、指针数组
1.指针数组的基本介绍
我们以前学过数值数组和字符数组
例如:
int arr[10]; 存放整形的数组 char arr[5]; 存放字符的数组
当数组元素的类型为指针类型的时候,成为指针数组
它的定义形式为
类型表示符 *数组名[常量表达式];
常量表达式代表元素的个数
例如:
int * arr[10]; 存放整型指针的数组 ,有10个元素
char *ch[5]; 存放字符指针的数组 ,有5个元素
2、指针数组的实例说明
int main()
{
int a = 10;
int b = 20;
int c = 20;
int* arr[3] = { &a,&b,&c };
for (int i = 0; i < 3; i++)
{
printf("%d ", *(arr[i]));
}
}
//结果为 10 20 20
这里都是定义了一个数组 arr 来存放a,b,c 的 地址,也就是指针
打印结果时也可以用for循环来实现
3、模拟二维数组
int main()
{
int i, j;
int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 2,3,4,5,6 };
int arr3[5] = { 3,4,5,6,7 };
int* p[3] = {arr1,arr2,arr3}; //数组名就是地址,不要用&地址符号,指针也就是地址
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
//printf("%d ", p[i][j]);
//printf("%d ", *(p[i]+j));
printf("%d ", *(*(p+i) + j));
}
printf("\n");
}
return 0;
}
printf("%d ", p[i][j]);
printf("%d ", *(p[i]+j));
printf("%d ", *(*(p+i) + j));
这三种写法都是对的
因为 p[i][j]==*(p[i]+j) p[i]==*(p+i)
二、数组指针
1.数组指针的基本介绍
我们以前学过
整形指针-- 指向整形的指针,存放整形变量地址的
int a=10; int *p=&a;
字符指针---指向字符的指针,存放的是字符变量的地址
char ch='w' ; int *p=&ch;
而现在我们要学习新的一种指针类型
数组指针----指向数组的指针
指向一行的数组指针变量,简称为行指针
定义形式 类型标识符(*指针变量名)[长度] (这里的长度是二维数组的列数)
例如: int (*p) [4];
p是一个行数组指针变量,一行有4个元素
注意:*先和p结合,说明p是指针变量,再指向int [4],说明它是指向int [4]的数组指针
[ ]的优先级高于*,如果没有括号,就变成了int* p[4]; 它是指针数组
2、数组指针的实例说明
int main()
{
int a[10] = { 0 };
printf("%p\n", a); //0000000B724FF7B8
printf("%p\n", a + 1); //0000000B724FF7BC
printf("%p\n", &a[0]); //0000000B724FF7B8
printf("%p\n", &a[0] + 1); //0000000B724FF7BC
printf("%p\n", &a); // 0000000B724FF7B8
printf("%p\n", &a + 1); //0000000B724FF7E0
return 0;
}
可以看出 数组名a 和 &a[0] 都能代表,数组的首元素地址。+1,就跳过了4个字节(因为sizeof(int)=4)
但&a(这里是取整个数组的地址)也是一样的结果,是因为数组的存储是从首地址开始的,
int (*p)[10]=&a(这里是取整个数组的地址) 把整个数组地址放到p指针里
&a+1 ,比 &a 跳过了40(4*10)个字节,这是跳过了int a[10] 这一整个数组
三、用数组指针存放一个数组的总地址
1,int arr[10];
//这是一个指针数组,有10个元素,每个元素的类型是 int
//这时想用一个指针来存放,这一整个指针数组的地址
//应写成
int(*p)[10] = &arr; //他就是一个 数组指针!!!,用来存放数组的全部地址
//括号里的* 说明了 p是一个,指针变量
//把(*p)去掉,剩下他的类型——int [10] 表明了 p这个指针指向的是 int [10] 这整个数组,这个数组有10个元素,元素类型为 int(整形)
2、int* arr[10];
//这是一个指针数组,有10个元素,每个元素的类型是 int*
//这时想用一个指针来存放,这一整个指针数组的地址
//应该要写成
int* (*p)[10] = &arr; //他就是一个 数组指针!!!,用来存放数组的全部地址
//括号里的* 说明了 p是一个,指针
// 把(*p)去掉,剩下的就是 他的类型—— int* [10] 说明,它是一个指针数组,它有10个元素,元素类型为 int* (也是指针)
四、数组指针数组(存放数组指针的数组)
int(*prr[10])[5];
// [ ]的优先级高于* ,所以prr先和,[10] 结合,它是一个数组,有10个元素
//通常我们都是去掉数组名来判断他的类型,
//去掉prr[10] 剩下它的类型——int (*) [5] ,
//说明了他的类型是一个,数组指针
//所以 可以发现,prr是一个存放数组指针的一个数组,它有10个元素,每个元素类型为 数组指针
五、一维数组传参(传的是首元素地址)
主函数如下
int main()
{
int arr1[10] = { 0 };
int* arr2[10] = { 0 };
test1(arr1);
test2(arr2);
return 0;
}
1.形参写成数组形式
void test1(int arr1[]) //[] 里面的元素个数课不写(写大了也不要紧)
{
}
void test2(int* arr[]) //[] 里面的元素个数课不写(写大了也不要紧)
{
}
2.形参写成指针形式
void test(int* p) //int 是arr1 数组一样的类型,
{ //*说明p是指针,p用来接收arr1数组的首元素的地址
}
void test(int** p) //int* 是arr2数组同一类型,
{ //第二个*说明,p是指针变量,p用来接收arr2数组的首元素地址
}
六、二维数组传参 (传的是首元素地址)
主函数如下
int main()
{
int arr[3][5];
test(arr);
return 0;
}
1.形参写成数组形式
void test(int arr[3][5]) //行数可以省略,列数不行
{
}
2.形参写成指针形式
void test(int(*p)[5]) // * 说明p是指针变量,他指向int [5] 这一数组 5个元素,
{ //类型为int(与原来的类型一致)
// [5] 这就是原来二维数组的列数,
} //这就是行指针,指向一行的一维数组
同理 char *[3][5] 传参时写成行指针 ——char*(*p)[5]
七、二维数组传参 (传的是整个二维数组地址)
void test1(int(*p)[5])
{
}
void test2(int(*p)[3][5]) //明确标明3行5列
{
}
int main()
{
int arr[3][5];
test1(arr); //传递的是首元素的地址
test2(&arr); //传递的是整个二维数组的地址时
return 0;
}
八、二级指针变量
void test(char** pp) //二级指针变量
{
}
int main()
{
char a = 'w';
char* p = &a;
char** pp = &p; //pp是一个二级指针
test(pp);
return 0;
}
九、函数指针
1.函数指针的写法
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*p) (int, int) = Add;
}
int test(char* str)
{
}
int main()
{
int (*p)(char*) = test;
}
对比可知,函数指针的写法
返回值类型,参数的类型都和函数定义函数时相同,一一对应, 但不要写出变量,只写类型标识符
2、函数指针的地址
可以看出,访问函数指针的地址加不加&都一样 函数地址也是他本身
3.函数指针的使用
可以看出*号个数有无,多少没什么区别,
但把Add看做地址的话,(*pt) 表示把它解引用,更好理解
调用函数指针的时候就像调用自定义函数一样
Add(函数名)相当于pt
十、函数指针数组(存放函数指针的数组)
1、函数指针数组介绍
pf先与[4]结合,[ ]比 * 优先级更高,说明 pf【4】是个数组,有4个元素
去掉pf[4] 剩下int(*)(int ,int ) 就是它的类型,函数指针,
pf【4】这个数组,有4个元素 类型都是函数指针
2、函数指针数组使用
以(计算器)为例
int (*p[5])(int, int) = { 0,Add,Sub,Mul,Div };
这里创建了这样一个函数指针数组,0相当于NULL (空指针)
这样的优点是很方便,不要用多个case 语句,
但是 前提是 函数指针的返回值类型,参数类型都是一致的
int Add(int i, int j)
{
return i + j;
}
int Sub(int i, int j)
{
return i - j;
}
int Mul(int i, int j)
{
return i * j;
}
int Div(int i, int j)
{
return i / j;
}
void meau()
{
printf("********************\n");
printf("***1.Add 2.Sub***\n");
printf("***3.Mul 4.Div***\n");
printf("*** 0.Exit ***\n");
printf("********************\n");
}
int main()
{
int (*p[5])(int, int) = { 0,Add,Sub,Mul,Div }; //函数数组的使用,简单,
//但要求了类型都一 样才能实现
int input = 1;
int a, b;
while (input)
{
meau();
printf("请选择:->");
scanf("%d", &input);
if (input >= 1 && input <= 4)
{
printf("enter two numbers\n");
scanf("%d%d", &a, &b);
int ret = p[input](a, b); //int ret = (*p[input])(a, b);
//都行,函数地 址相当函数本身
printf("结果为%d\n", ret);
}
else if(input ==0)
{
printf("程序退出\n");
break;
}
else
printf("请重新输入\n");
}
return 0;
}
十一、指向函数指针数组的指针(函数指针数组指针)
1、介绍
(1)、类比:
int a[10];
int(*p)[10] = &a;
(2)、推理
int (*p1[4])(int, int);
//函数指针数组,它有4个元素,每个元素类型为 (去掉p[4]看类型) ----- (int (*)(int ,int )
//这时又要有一个指针指向这个数组(函数指针数组) ,来存放这个数组的地址,
//应该写成
int (*(*p2)[4])(int, int)=&p1;
//去掉(*p2)[4]看类型,为int(*)(int ,int)
//p2是一个指向函数指针数组的指针
详细如图:
2、使用
int Add(int i, int j)
{
return i + j;
}
int Sub(int i, int j)
{
return i - j;
}
int Mul(int i, int j)
{
return i * j;
}
int Div(int i, int j)
{
return i / j;
}
int main()
{
int (*pfarr[4])(int, int) = { Add,Sub,Mul,Div };
int (*(*p)[4])(int, int) = &pfarr;
for ( int i = 0; i < 4; i++)
{
int ret = (*p)[i](6, 2);
printf("%d\n", ret);
}
return 0;
}
int Add(int i, int j)
{
return i + j;
}
int Sub(int i, int j)
{
return i - j;
}
int Mul(int i, int j)
{
return i * j;
}
int Div(int i, int j)
{
return i / j;
}
int main()
{
int (*pfarr[4])(int, int) = { Add,Sub,Mul,Div };
int (*(*p)[4])(int, int) = &pfarr; //函数指针数组指针
for ( int i = 0; i < 4; i++)
{
int ret = (*p)[i](6, 2);//解引用p到pfarr 用i依次使用加,减,乘,除
printf("%d\n", ret);
}
printf("\n");
for (int i = 0; i < 4; i++)
{
int ret = pfarr[i](6, 2); //直接使用函数指针数组
printf("%d\n", ret);
}
return 0;
}
直接使用函数指针数组和解引用函数指针数组指针效果一样