目录
1、指针的概念
2、一级指针
2.1 一级指针的定义方式
2.2指针变量指向的类型的作用
2.3一级指针的使用
3、指针数组
3.1指针数组的概念
3.2指针数组的简单运用
4、数组指针
4.1数组指针的概念
4.2数组指针的简单使用
5、指针函数
5.1指针函数的概念
5.2指针函数的简单使用
6、函数指针
6.1函数指针的概念
6.2函数指针使用
7、函数指针数组
7.1函数指针数组的概念
7.2函数指针数组的简单使用
8、野指针
8.1野指针的概念
8.2消除野指针的方式
9、二级指针以及多级指针
9.1二级指针的概念
9.2二级指针的简单使用
题外话
本意是记录一下自己对指针的一些理解,以及希望通过自己的讲解,能够让各位初学指针的小伙伴更迅速的认识指针。这个是初版,后期会定期进行修改,有误的地方还请各位大侠指正,废话不多说,咱们直接进入正题。
1.指针的概念
那么什么是指针呢?指针就是地址,地址就是指针,一定要记住这个概念。要讲解指针,我们得从内存说起。把内存假想成一栋楼如下:
0x00000004 | 4 |
0x00000003 | 3 |
0x00000002 | 2 |
0x00000001 | 1 |
0x00000000 | 0 |
你家的楼总共有五间房间,每间房间有一个相应的门牌号0x00000000~0x00000004,每一间房间分别存放了0~4这五个数字。如果我们想要知道2在哪里,只需要告诉我们2对应的门牌号,那么我们就可以找到相对应的房间,进一步找到2, 对吧。这个门牌号就是我们所说的地址。
咱们来看一个例题,在c语言中我们用&符号取变量的地址:
上图中,大家都知道num在内存空间占的字节数为4,四个字节各有一个地址,那么&取的究竟是哪一个地址呢?继续看上图我们令&num+1所跨越的字节数是4,也就是说,&取的是首地址,那么为什么取的是首地址呢?
举一个简单的例子,你的房间有100W,你给我说你的房间号0x62FE18,让我去打开你的房间拿钱,那我怎么知道0x62FE18房间号在哪里呢,你得告诉我你家的首地址我才能知道你家具体的位置,才能去到你家找到你的房间0x62FE18,是吧,那么是不是通俗易懂了呢,哈哈。
找到了你的房间,但是我又没有钥匙,我进不去你的房间呀,100W拿不出来,所以我得带一把钥匙 * ,这个符号是不是特别像钥匙呢,那我就用这把钥匙打开0x62FE18号房间的门,*(&num),得到的结果是100,是不是把钱取出来了,哈哈,结果如下图,请读者自行进行编程实践,实践见真知嘛:
通过上面两个图做比较:num = *(&num),num是变量,而*(&num)是取&num这个地址空间里面的存放的值。
接下来咱们继续看一个例子:
可以直观的看到&num = p,也就是说指针变量p存储的是num的地址,所以指针就是地址,地址就是指针;而num 是等于 *p的,*p代表取p所指向的地址&num空间所存放的值,这样是不是就比较容易理解指针了呢?同学们。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
记住指针就是地址,地址就是指针。。。。。。。。。。。。。。。。。。。。。。。。。。。。
2、一级指针
2.1 一级指针的定义方式
1.数据类型* 变量名称 2.数据类型 *变量名称;
2.2 指针变量指向的类型的作用
#include<stdio.h>
#include<stdlib.h>
int main()
{
int ret =0;
int num = 100; //定义一个整型普通变量
int *p = # //定义一个指针变量并且该指针变量指向了num的地址
printf("num的地址是:0x%X\n",&num);
printf("p的值是:0x%X\n",p);
printf("num的值是:%d\n",num);
printf("*p的值是:%d\n",*p);
exit(ret);
}
2.2.1指针变量自身的类型
定义一个指针变量int *p,将指针变量去掉,剩下的是啥类型就是啥类型。p指向自身的类型是int *;
2.2.2指针指向的类型
定义一个指针变量int *p,将指针变量以及与离它最近的一个*去掉,剩下的是啥类型就是啥类型。p指向的类型是int ;
2.2.3指针变量所指向的类型有什么作用呢?
(1)决定指针变量+1的跨度,下面咱们来看一个示例:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int ret = 0;
int num = 100;
int *p = #
char *p1 = #
printf("&num = 0x%x\n",&num);
printf("p+1 = 0x%x\n",p+1);
printf("p1+1 = 0x%x\n",p1+1);
exit(ret);
}
在linux下输出为:
我们可以看到,p指向的类型是int,p+1的时候跨越了四个字节;而p1指向的类型是char,p1+1的时候跨越了一个字节,所以指针变量所指向的类型决定指针变量+1的跨度。
2.3一级指针的使用
例1:指针作为形参做一个简单的加法运算;指针作为形参改变主函数中普通变量的值
#include<stdio.h>
#include<stdlib.h>
void fun(int *p1,int *p2)//指针作为形参做加法运算
{
printf("%d\n",*p1 + *p2);
}
void reform(int *num)//指针作为形参改变主函数定义的变量的值
{
printf("在函数内部的值:%d\n",*num);
*num = 1000;
}
int main()
{
int ret = 0;
int m = 3;
int n = 4;
int num = 100;
fun(&m,&n);
reform(&num);
printf("在主函数输出num的值:%d\n",num);
exit(ret);
}
输出结果:
当然以上指针的使用都特别简单,如果想更深入使用,请移步其他大佬的文章。
3、指针数组
3.1指针数组的概念
指针数组、是数组,是用于储存地址的数组,数组的每一个元素都是指针。定义的方式为:
数据类型 *指针变量[ ]。例如int *p[10]; 由于呢[ ]的优先级比*高,因此[ ]先和p组成数组,数组的每一个元素呢都是指针。因此指针数组是数组。
3.2指针数组的简单运用(低级用法)
#include<stdio.h>
#include<stdlib.h>
int main()
{
int ret = 0;
//定义两个数组
char str1[] = "abcdefg";
char str2[] = "hijklmn";
//定义一个指针数组存放str1和str2这两个数组的首地址
char *str3[] = {str1,str2};
int i = 0;
int j = 0;
while(i<2)
{
while(j<sizeof(str1))
{
printf("%c",*(str3[i]+j));//输出指针数组所存放的值
j++;
}
printf("\n");
j = 0;
i++;
}
exit(ret);
}
输出结果为:
首先明确str1和str2代表的是什么,str1代表的是str1这个数组的首元素的首地址,&str1代表的则是str1数组的首地址。。。。至于str3[i]+j的意思请移步2.2指针变量所指向的类型的作用。
4、数组指针
4.1数组指针的概念
数组指针是指针,定义的方式为:数据类型 (*变量名)[ ] ,例如:int (*p)[ 10 ],( )的优先级比[ ]高,因此p是指针,指向了有10个int类型数据的数组。
4.2数组指针的简单运用(低级运用)
#include<stdio.h>
#include<stdlib.h>
int main()
{
int ret = 0;
//定义一个3*3的数组
int str1[9] = {1,2,3,4,5,6,7,8,9};
int (*str3)[9] = &str1;
int i = 0;
int j = 0;
while(i<1)
{
while(j<9)
{
printf("%d",(*str3)[j]);//输出指针数组所存放的值
j++;
}
printf("\n");
j = 0;
i++;
}
exit(ret);
}
输出结果:123456789。当然有多种输出方式吗,我只是使用了其中一种。
5、指针函数
5.1指针函数的概念
指针函数是返回值为指针的函数,定义方式为:数据类型 *函数名();例如:int *fun(int *p);
5.2指针函数的简单使用
6、函数指针
6.1函数指针的概念
函数指针,本质是一个指针变量,该指针指向一个函数。总之,函数指针就是指向函数的指针。声明格式例如:类型说明符 (*指针变量)(函数入口参数),例如:int (*p)(int a,int b);
6.2函数指针使用
需要将一个函数的地址赋值给指针变量
int (*p)(int a,int b); int swap(int x,int y); p = swap 或者 p = &swap;
以上取地址符不是必须的,因为一个函数 标识符 就表示了函数的地址,建议使用第二个方式。 函数指针在嵌入式中应用广泛,常常把函数指针作为结构体成员、作为函数的参数等。
程序示例如下图所示:
函数指针作为结构体成员,作一个四则运算如下:
函数指针作为函数的入口参数,作一个示例如下所示
以上作为参数传递的函数指针通常表示回调函数(Callback Function): 什么是回调函数? 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
7、函数指针数组
7.1函数指针数组的概念
和指针数组一样,函数指针数组是数组,数组中每一个元素是指针,是指向函数地址的指针,定义方式如下所示:数据类型 (*指针变量名[ ])();
7.2函数指针数组的简单使用
#include<stdio.h>
#include<stdlib.h>
void fun1();
void fun2();
void fun3();
void fun4();
//定义一个函数指针数组 该数组有四个元素 每一个元素分别指向了函数的地址
void (*p[4])() = {fun1,fun2,fun3,fun4};
int main()
{
int ret = 0;
int m = 0;
while(m<4)
{
(*p[m])();//逐个输出函数里面的内容
m++;
}
exit(ret);
}
void fun1()
{
printf("我是第一个函数\n");
}
void fun2()
{
printf("我是第二个函数\n");
}
void fun3()
{
printf("我是第三个函数\n");
}
void fun4()
{
printf("我是第四个函数\n");
}
输出结果:
8、野指针
8.1野指针的概念
8.1.1.什么是野指针?
(1)定义指针并没有进行初始化,就被初始化为随机值,那么当对这个随机值进行访问的时候,这个时候这个值是不确定的,这就导致了野指针。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int ret = 0;
int m = 100;
int *p; //并未进行初始化
printf("%d\n",*p);
exit(ret);
}
(2)指针越界也会导致野指针的问题,越界之后访问的是非法的空间,也就导致野指针问题。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int ret = 0;
int arry[5] = {1,2,3,4,5};
int *p = arry;
for(int i = 0;i<10;i++)
{
printf("%d ",*(p+i));//指针越界
}
exit(ret);
}
(3)指针指向的空间被释放,导致野指针。
#include<stdio.h>
#include<stdlib.h>
int *fun()
{
int a = 100;
return &a;
//因为局部变量是在栈区开辟空的间 函数结束空间即销毁
}
int main()
{
int ret = 0;
int *p = fun();//空间销毁 指针指向无效地址
printf("%d",*p);
exit(ret);
}
8.2消除野指针的方式
(1)声明指针变量的时候进行初始化 (2)指针不越界使用 (3)不使用指针的时候使指针指向NULL,指针指向NULL的方式有 free,delete。
9、二级指针以及多级指针 (这里以后再补充)
9.1二级指针的概念
9.2二级指针的简单使用