一.指针数组和数组指针的区别
指针数组可以理解为指针的数组,侧重点在后面的数组,所以指针数组其实就是存放指针的数组,本质上其实就是个数组而已
同样数组指针也可以也可以加个的字来理解,即数组的指针,数组的指针本质上就是个指针,那么数组指针变量存放的其实就是数组的地址,是能够指向数组的指针变量。
指针数组变量的表现形式是int * (变量名)[数组的大小],例如int *arr[5],这个数组5个元素,每个元素类型是int *(即整型指针类型)
指针数组的每个元素是地址,可以指向⼀块区域
数组指针变量的表现形式是 int (*变量名)[指向数组的大小],例如int(*parr)[10],指针指向的是数组,这个数组有10个元素。
int (*p) [10] = &arr;
[10]是p指向数组的元素个数
p是数组指针变量名
int是p指向的数组的元素类型
数组指针的类型是int(*)[10];去掉指针变量的名字就是它的类型了
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int(*p)[10] = &arr;//指针变量p保存整个数组的地址
return 0;
}
从编译器调试中可以看出p此时保存的是整个数组的地址,所以类型和内容相同。
通过二维数组来帮助理解一下指针数组和数组指针
二维数组可以看作是每个元素都是一维数组的数组
所以二维数组的首元素地址其实就是第一行整个数组的的地址
一维数组传参传的是单个首元素地址,例int arr[]={1,2,3,4,5,6},此时首元素是int类型的1的地址,所以函数形参接收用得是指向int类型的指针变量去接收它,即int *
#include<stdio.h>
void Printf(int* arr, int ret)//接收的是int类型首元素地址
{
int i = 0;
for (i = 0; i < ret; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6 };
int ret = sizeof(arr) / sizeof(arr[0]);
Printf(arr, ret);
}
二维数组同样用上述打印的函数,传递的也是首元素地址,只不过不是单个元素了,而是一整个第一行数组的地址,这时候就得用数组的指针去接收这一整个数组的地址了
#include<stdio.h>
void Printf(int(*p)[3], int r,int c)//接收的是一整个数组的元素,第一行整个一维数组类型是int [3],用int(*)[3]类型的数组指针来接收
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < c; j++)
{
printf("%d ", *(*(p+i)+j));//p+i解引用相当于此时在第i行,而+j解引用才是打印第每一行上具体的元素
//j循环都打印完了i++p指针下移到第二行开始打印第二行,一开始p指针指向第一行
}
printf("\n");
}
}
int main()
{
int arr[2][3] = { {1,2,3},{4,5,6} };
int ret = sizeof(arr) / sizeof(arr[0]);
Printf(arr,2,3);
}
二维数组是多个一维数组组成的数组,那么是否可以通过先建成多个一维数组,然后把一维组合成二维数组呢。这通过指针数组就可以来实现,基体操作如下
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3 };
int brr[] = { 4,5,6 };
int nrr[] = { 7,8,9 };
int* pre[3] = { arr,brr,nrr };//请注意这里面存的是上面数组的地址(指针),所以用指针的数组来表示
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 3; j++)
{
printf("%d", pre[i][j]);
}
printf("\n");
}
}
但是值得注意的是利用指针数组建成的二维数组并不是真正的二维数组,只是模拟二维数组的表现形式而已。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二.字符指针和函数指针介绍
(1).字符指针
单个字符和字符指针变量的表现形式
#include<stdio.h>
int main()
{
char ch = 'v';
char* p = &ch;
return 0;
}
字符串和字符指针变量的表现形式
#include<stdio.h>
int main()
{
const char* pre = "hello world";//不是把整个字符串存放到指针变量中,而是放的首字母地址
printf("%s", pre);
return 0;
}
字符指针变量存字符串地址的时候,只是保存首字母的地址,不是把整个字符串放到字符指针中。可以把字符串想象为一个字符数组,但是这个数组不能修改,所以要用const来修饰。字符指针指向常量字符串时,常量字符串不会存多块,因此是同一个地址。同时,内容相同的常量字符串只会保存一份,只会开辟一个空间。
(2).函数指针
函数指针变量是用来存放函数地址的,未来通过地址可以调用函数
函数名就是函数的指针,也可以通过&函数名来获取函数地址
int (*pf3) (int x, int y)
(int x,int y)是pf3指向函数的参数类型和个数的交代
pf3是函数指针变量名
int是pf3指向函数的返回类型
int (*) (int x, int y) 就是pf3函数指针变量的类型
创建函数指针变量的两种写法
void test()
{
printf("hehe\n");
}
void (*pf1)() = &test;第一种
void (*pf2)()= test;第二种
int Add(int x, int y)
{
return x+y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add; //x和y写上或者省略都是可以的
函数指针变量的使用:通过函数指针来调用指针指向的函数
调用指向的函数可以用指针变量解引用可以不解引用
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf3)(int, int) = Add;//函数指针保存函数的地址
printf("%d\n", (*pf3)(2, 3));
printf("%d\n", pf3(3, 5));
return 0;
}
(3).二级指针
地址是通过指针变量来保存的,而指针变量也是变量,它在初始化的时候也会分配相应地址空间。如果我要保存指针变量的地址,那该怎么做呢。要保存地址就得使用指针变量,意思就是用另一个指针变量去保存这个指针变量的地址,这就是二级指针
上图一级指针表现形式是int *pa,而二级指针表现形式是int **ppa,只是多了一个*号而已
一级指针解引用是*pa,得到a的值10,而二级指针解引用*ppa,得到的是一级指针变量pa保存的内容(也就是a的地址),再解引用一次**ppa,得到的依旧也是a的值10