首先在对指针进行理解之前,我们要清楚下面这几个个概念:
和现实生活对空间的分别方式相似,给不同的地域不同的名称。例如,北京,上海等
而内存也是一块空间,所以对这块空间的不同位置进行不同的编号,用起来也会更加方便。
这些编号就是我们常说的地址了。
我们都知道 ,内存里最小的内存单元就是一个bit位 ,那多大的内存空间可以分配一块地址呢,在c语言的标准中规定,给每个字节大小的空间(8个比特位)配置一个专门的地址。
在c程序中创建一个变量a(这里我假设它的地址为0xff112233),那么我们通常是用变量名来访问这个数据,但是也可以用地址来访问这个数据 。那么要怎么通过地址来访问数据呢?????
这里就需要指针了。
#include
int main()
{
//一级指针
int a = 10;
int* pa = &a;
printf("%d\n", a);
printf("%d\n", *pa);
return 0;
}
int a = 10;//定义一个整形变量,变量中存储的数据是10
int* pa = &a;//* 代表pa是一个指针 最前面的int代表pa指向的变量类型是整形类型
*pa=1;//代表对pa中的数据(也就是刚才拿到的a的地址)进行解引用,来访问a中的数据。
总结:指针里只能存放地址,地址也只能放置在指针中,对一级指针进行解引用就能通过他里面的地址来访问原变量a中存储的数据
通过前面的了解我们知道,指针变量里面放置的是地址,而只要是变量,那肯定就有它自己的地址
所以我们来看下面这个例子
**ppa也可以看成 *(*ppa) 首先*ppa表示对ppa中的数据进行解引用,就能访问pa中存储的数据 所以*ppa就想当于pa,而对pa进行解引用就能访问a了(在上面的例子我们可以看到这两钟形式打印出的数据一样)
所以 * *ppa 也就相当于 a 了
总结:
一级指针pa里存的是整形变量a的地址,二级指针ppa里存的是一级指针变量pa的地址。
以此类推n级指针的定义和访问方式相同。
int arr[10];
//这里表示arr是一个数组,【10】表示数组里有10个元素,int表示这个数组里面元素的类型是int型的。
数组的数组名存放的是这个数组首元素的地址。我们在通过数组名来访问数组中的数据的时候,
arr【0】在编译器看来就相当于 *(arr+0)
arr【0】<=>*(arr+0),这两种写法是完全等价的。
创建和访问的方式与普通数组相同
在了解这个之前我们来看一个整形数组的概念
int arr[10];//这里表示arr是一个数组,【10】表示数组里有10个元素,int表示这个数组里面元素的类型是int型的。
了解这个概念,我们来对比指针数组的概念就很好理解了
int * arr【10】;//这里表示arr是一个数组,【10】表示数组里面有10个元素,* 表示这个数组中存储的数据类型是指针,int 代表这些指针所指向的数据类型是int型的。
#include
int main()
{
//数组指针
int arr[10] = {0 ,1,2,3,4,5,6,7,8,9};
int (*parr)[10]=&arr;
//这里parr就是arr数组的指针,简称数组指针
for (int i = 0; i < 10; i++)
{
printf("%d ",arr[i]);
}
printf("\n");
for (int i = 0; i < 10; i++)
{
printf("%d ", *((*parr) + i));
}
printf("\n");
for (int i = 0; i < 10; i++)
{
printf("%d ", (*parr)[i]);
}
printf("\n");
return 0;
}
运行结果
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
int arr[10] = {0 ,1,2,3,4,5,6,7,8,9};//创建一个整形数组,里面有10个元素
int (*parr)[10]=&arr;1 &arr 拿出了arr这个整形数组的地址
2 (*parr)因为刚才拿出的是数组的地址,所以我们需要一个指针来接受
3 【10】代表这个指针指向的是一个包含10个数据的数组
4 int 代表这个数组的类型是整形
综上 parr就是这个arr数组的指针,简称为数组指针。(指向数组的指针)
好了 ,有上面的基础,我们可以类比的来写出 ,double * arr1【5】的数组指针了
1 :要接受一个地址, 那么我们需要一个指针 (*parr1)。
2 :【5】这个指针指向的是一个有五个元素的数组
3 : double *表示这个数组里的元素类型是指向double类元素的指针
double*(*parr1)【5】=&arr1;
这里parr1就是数组double * arr1【5】的指针了。
他的定义类型和数组指针类似。
void ment();//函数声明
void (*p)()=&ment;&ment 取出函数ment的地址,
(*p)既然是一个地址,那么我需要一个指针来接收
()表示这个指针指向的是一个函数,里面没有参数,函数不需要传参
void 表示函数的返回类型。
下面再来一个有参数有返回类型的例子。
当然,还有一种方式来创建函数指针
int (*pa)(int ,int )=ADD;
直接将函数名中的值赋给指针pa,因为对函数来说,函数不像数组,他没有首元素概念,他里面的地址就是函数本身的地址。
定义如下:
int((*parr[10]))(int, int) ;
详细解释如下:
那像这样的复杂数组有什么用呢,这里就要知道他的另一个名称了 转移表
下面我们通过一个简单的计算器程序来了解一个他的用法
#include
void ment()
{
printf("*************************************\n");
printf("*************************************\n");
printf("******** 1 : ADD 2 : SUB *********\n");
printf("******** 3 : MLU 4 : ESC *********\n");
printf("********** 0 : EXIT ************\n");
printf("*************************************\n");
printf("*************************************\n");
}
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mlu(int x, int y)
{
return x * y;
}
int esc(int x, int y)
{
return x / y;
}
int main()
{
int((*parr[10]))(int, int) = { NULL,add,sub,mlu,esc };
//转移表
int input;
int x, y;
do
{
ment();
printf("请选择:\n");
scanf("%d", &input);
if (input >= 1 && input <= 4)
{
printf("请输入两操作数:");
scanf("%d%d", &x, &y);
printf("ret = %d\n", (parr[input])(x, y));
}
if (input == 0)
{
printf("退出程序:\n");
}
else
{
printf("选择错误,请重新选择:》\n");
}
} while (input);
return 0;
}
int((*parr[10]))(int, int) = { NULL,add,sub,mlu,esc };
这里我们在对各种功能的函数进行调用时,我们可以先设置好函数的位置,然后直接通过数组下标来访问这个函数(这里和switch语句很相似,但是这样编写代码,可以大大减少代码量)
定义方式如下:
int((*parr[10]))(int, int) = { NULL,add,sub,mlu,esc };
int (*((*p)[10]))(int,int) = &parr;
详解如下
这里p先和*结合,代表p是一个指针
【】代表这个指针所指向的类型是一个数组 10 代表这个数组中有10个元素
((*p)【10】)外面的那个*代表这个数组中的元素类型是指针
(int ,int)代表指针指向的是一个函数,而函数的参数类型是两个int
最前面的那个int代表这个函数的返回类型是int型