欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > C语言指针·高级用法超详解(指针运算、野指针、悬空指针、void类型指针、二级以及多级指针)

C语言指针·高级用法超详解(指针运算、野指针、悬空指针、void类型指针、二级以及多级指针)

2024/10/25 6:33:19 来源:https://blog.csdn.net/MANONGDKY/article/details/140813858  浏览:    关键词:C语言指针·高级用法超详解(指针运算、野指针、悬空指针、void类型指针、二级以及多级指针)

目录

1.  指针的运算

2.  野指针和悬空指针

2.1  野指针

2.2  悬空指针

3.  void类型指针

4.  二级指针和多级指针

4.1  命名规则

4.2  作用

4.2.1  二级指针可以操作一级指针记录的地址

4.2.2  利用二级指针获取变量中记录的数据


1.  指针的运算

文章开始前可以先了解:C语言指针·入门用法超详解-CSDN博客

        通过上一章我们初步了解了指针的用法,知道指针的类型是:

        其中指针中数据类型的作用是获取字节数据的个数,正常情况下变量p只能获取到0x001的内存地址,而int型数据占4字节,因此:

        那么我们可以思考一下,若是:

int a=10;
int* p=&a;p+1

        假设a的地址为:0x01,那么p+1指向的地址会是什么呢?

        会是0x02还是0x05呢?

        正确答案应该是:0x05,这是为什么呢?

步长:指针移动一次的字节个数

        因为,指针的加法操作实际上是将指针向后移动若干个存储单元,而不是简单的数学加法,上面我们也提到了正常情况下变量p只能获取到0x01的内存地址,而int型数据占4字节,而p+1就是指针移动一步,一步在这里是四个字节,因此p+1,最终会移动到0x05的位置。我们拿代码验证一下:

#include <stdio.h>int main()
{/** 指针的运算* 步长:指针移动一次,走了多少字节* char:1* short:2* int:4* long:4* long long:8*///加法:指针往后移动了N步P+N//减法:指针往前移动了N步P-Nint a = 10;int* p = &a;printf("%p\n", p);printf("%p\n", p + 1);printf("%p\n", p + 2);printf("%p\n", p - 1);printf("%p\n", p - 2);return 0;
}


注意:

指针有意义的操作:

        指针跟整数进行加、减操作(每次移动N个步长)

        指针跟指针进行减操作(间隔步长)

#include <stdio.h>int main()
{/** 有意义的操作:指针跟整数进行加、减操作(每次移动N个步长)指针跟指针进行减操作(间隔步长)无意义的操作:指针跟整数进行乘除操作原因:此时指针指向不明指针跟指针进行加、乘、除操作*///前提条件:保证内存空间是连续的//数组int arr[] = { 1,2,3,4,5,6,7,8,9 };//获取0索引的内存地址int* p1 = &arr[0];//指针跟整数进行加、减操作(每次移动N个步长)//通过内存地址获取数据printf("%d\n", *p1);printf("%d\n", *(p1 + 1));//获取5索引的内存地址int* p2 = &arr[5];//p2 - p1间隔了多少步长printf("%d\n", p2 - p1);printf("%p\n", p1);printf("%p\n", p2);return 0;
}

指针无意义的操作:

        指针跟整数进行乘除操作

        原因:此时指针指向不明

        指针跟指针进行加、乘、除操作


2.  野指针和悬空指针

2.1  野指针

        野指针:指针指向的空间未分配。

#include <stdio.h>int main()
{/*野指针:指针指向的空间未分配悬空指针:指针指向的空间已分配,但是被释放了*///野指针:指针指向的空间未分配int a = 10;int* p1 = &a;printf("%p\n", p1);printf("%d\n", *p1);//p2为野指针int* p2 = p1 + 10;printf("%p\n", p2);printf("%d\n", *p2);return 0;
}

        此时运行程序,虽然也能得到p2的地址,但是要知道,这块地址我们并没有给他分配,他虽然能找到地址,但是若是随意修改数据,在正常使用中,可能会修改别的函数的数据,导致最终运行错误: 

2.2  悬空指针

        悬空指针:指针指向的空间已分配,但是被释放了。

#include <stdio.h>int* method();int main()
{/*野指针:指针指向的空间未分配悬空指针:指针指向的空间已分配,但是被释放了*///悬空指针:指针指向的空间已分配,但是被释放了int* p3 = method();printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("%p\n", p3);printf("%d\n", *p3);return 0;
}int* method()
{int num = 10;int* p = &num;return p;
}

        会发现此时代码数据错误,正常p3应当显示10: 

        这里可以调用static来使用:

#include <stdio.h>int* method();int main()
{/*野指针:指针指向的空间未分配悬空指针:指针指向的空间已分配,但是被释放了*///悬空指针:指针指向的空间已分配,但是被释放了int* p3 = method();printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("%p\n", p3);printf("%d\n", *p3);return 0;
}int* method()
{static int num = 10;int* p = &num;return p;
}

3.  void类型指针

特殊类型:void* p

不表示任何类型,只能记录地址值,不能获取到变量里面的数据,也不能进行加减的计算。

特点:无法获取数据,无法计算,但是可以接收任意地址数据。

        在我们使用指针的过程中会发现,不同类型的指针之间是不能相互赋值的,否则会发生编辑错误:

#include <stdio.h>int main()
{//void类型指针//定义两个变量int a = 10;short b = 20;//定义两个指针int* p1 = &a;short* p2 = &b;//输出打印printf("%d\n", *p1);printf("%d\n", *p2);//不同类型的指针之间是不能相互赋值的char* p3 = p1;printf("%d\n", *p3);//这里编译器为我们进行了强制类型转换,上面的过程相当于如下char* p4 = (char*)p1;printf("%d\n", *p4);return 0;
}

         这里的指针间进行复制那是因为编译器在编译过程中,对p1进行了强制类型转换,p3的实际流程,如p4所示:

        为了解决这一问题我们可以将数据类型定义为void,不过这也会导致,不能printf输出数据,因为void不能获取到变量里面的数据,也不能进行加减的计算,否则编译器会报错:

#include <stdio.h>int main()
{//void类型指针//定义两个变量int a = 10;short b = 20;//定义两个指针int* p1 = &a;short* p2 = &b;//输出打印printf("%d\n", *p1);printf("%d\n", *p2);//不同类型的指针之间是不能相互赋值的char* p3 = p1;printf("%d\n", *p3);//这里编译器为我们进行了强制类型转换,上面的过程相当于如下char* p4 = (char*)p1;printf("%d\n", *p4);//void类型指针打破上面的观念//void没有任何类型,好处可以接收任意类型指针记录内存地址void* p5 = p1;void* p6 = p2;//缺点:不能获取到变量里面的数据,也不能进行加减的计算//printf("%d\n", *p5);//printf("%d\n", *p6);//printf("%d\n", *p5 - 1);//printf("%d\n", *p6 + 1);return 0;
}

        那么我们引用void有什么用呢?

        例如,我们用于调用函数进行交换数据,我们可以编写如下代码:

#include <stdio.h>void swap(void* p1, void* p2);int main()
{//void类型指针//定义两个变量int a = 10;short b = 20;//定义两个指针int* p1 = &a;short* p2 = &b;//输出打印printf("%d\n", *p1);printf("%d\n", *p2);//不同类型的指针之间是不能相互赋值的char* p3 = p1;printf("%d\n", *p3);//这里编译器为我们进行了强制类型转换,上面的过程相当于如下char* p4 = (char*)p1;printf("%d\n", *p4);//void类型指针打破上面的观念//void没有任何类型,好处可以接收任意类型指针记录内存地址void* p5 = p1;void* p6 = p2;//缺点:不能获取到变量里面的数据,也不能进行加减的计算//printf("%d\n", *p5);//printf("%d\n", *p6);//printf("%d\n", *p5 - 1);//printf("%d\n", *p6 + 1);//调用函数交换数据int c = 100;int d = 200;swap(&c, &d);printf("c=%d,d=%d\n", c, d);return 0;
}//函数:用来交换两个变量记录数据void swap(int* p1, int* p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}

         此时可以实现数据的交换:

        但是若是此时我想使用long long型数据呢?数据会发生报错(我在DevC++上运行报错,在VS上运行正确,因为VS在这里编辑器给他自动强制类型转换了):

#include <stdio.h>void swap(void* p1, void* p2);int main()
{//void类型指针//定义两个变量int a = 10;short b = 20;//定义两个指针int* p1 = &a;short* p2 = &b;//输出打印printf("%d\n", *p1);printf("%d\n", *p2);//不同类型的指针之间是不能相互赋值的char* p3 = p1;printf("%d\n", *p3);//这里编译器为我们进行了强制类型转换,上面的过程相当于如下char* p4 = (char*)p1;printf("%d\n", *p4);//void类型指针打破上面的观念//void没有任何类型,好处可以接收任意类型指针记录内存地址void* p5 = p1;void* p6 = p2;//缺点:不能获取到变量里面的数据,也不能进行加减的计算//printf("%d\n", *p5);//printf("%d\n", *p6);//printf("%d\n", *p5 - 1);//printf("%d\n", *p6 + 1);//调用函数交换数据long long c = 100L;long long d = 200L;swap(&c, &d);printf("c=%lld,d=%lld\n", c, d);return 0;
}//函数:用来交换两个变量记录数据void swap(int* p1, int* p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}

        那么我们如何才能进行不同类型都能调用该函数呢?可以使用void类型,

void swap(void* p1, void* p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}

        但是如果使用void又会出现新的问题就是:void不能获取到变量里面的数据,也不能进行加减的计算,那么要如何解决呢?

           这里将void*类型的指针p1和p2分别转换为char*类型的指针pc1和pc2。这是因为char类型是 C 语言中的基本数据类型,且大小为1字节,所以可以通过char*指针逐字节访问内存。     

        同时函数void swap(void* p1, void* p2, int len)加上一个len用于表示字节数,要是int型len就等于4,long long就是等于8(在x64环境下):

void swap(void* p1, void* p2, int len)
{//把void类型的指针,转换char类型的指针char* pc1 = p1;char* pc2 = p2;char temp = 0;//以字节为单位,一个字节一个字节的进行转换for (int i = 0; i < len; i++){temp = *pc1;*pc1 = *pc2;*pc2 = temp;pc1++;pc2++;}
}

        完整程序代码: 

#include <stdio.h>void swap(void* p1, void* p2, int len);int main()
{//void类型指针//定义两个变量int a = 10;short b = 20;//定义两个指针int* p1 = &a;short* p2 = &b;//输出打印printf("%d\n", *p1);printf("%d\n", *p2);//不同类型的指针之间是不能相互赋值的char* p3 = p1;printf("%d\n", *p3);//这里编译器为我们进行了强制类型转换,上面的过程相当于如下char* p4 = (char*)p1;printf("%d\n", *p4);//void类型指针打破上面的观念//void没有任何类型,好处可以接收任意类型指针记录内存地址void* p5 = p1;void* p6 = p2;//缺点:不能获取到变量里面的数据,也不能进行加减的计算//printf("%d\n", *p5);//printf("%d\n", *p6);//printf("%d\n", *p5 - 1);//printf("%d\n", *p6 + 1);//调用函数交换数据int c = 100;int d = 200;swap(&c, &d, 4);printf("c=%d,d=%d\n", c, d);return 0;
}//函数:用来交换两个变量记录数据
/*
void swap(int* p1, int* p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}
*///修改一下函数,更具有通用性
//因为以上函数,若是主函数调用的话,只能int类型的数据
void swap(void* p1, void* p2, int len)
{//把void类型的指针,转换char类型的指针char* pc1 = p1;char* pc2 = p2;char temp = 0;//以字节为单位,一个字节一个字节的进行转换for (int i = 0; i < len; i++){temp = *pc1;*pc1 = *pc2;*pc2 = temp;pc1++;pc2++;}
}

4.  二级指针和多级指针

4.1  命名规则

        以二级指针为例,上一章最初我们了解了什么是指针?

C语言指针·入门用法超详解-CSDN博客

         那么既然普通变量就有指针,指针也是一个变量为什么不能有一个指针继续指向他呢?那么我们可以了解到:

        既然指针也有他的指针,那么指针的指针要怎么命名呢?

        其和指针的命名规则是一样的也是:

数据类型  *  变量名;

        但是需要注意的是:指针的数据类型要跟指向空间中的数据的类型保持一致。

        首先对于指针p,其指向数据a的地址,需要和a数据类型保持一致,那么指针的命名就是:

int                       *                    p;

数据类型           标记            变量名

        然后,二级指针指向的是指针的数据类型,此时指针空间内存储的不是数据,而是a的地址,他的数据类型是int*,也就是说二级指针的命名:

int*                       *                    p;

数据类型           标记            变量名

4.2  作用

4.2.1  二级指针可以操作一级指针记录的地址

        上代码:

#include <stdio.h>int main()
{//定义变量int a = 10;int b = 20;//定义一级指针int* p = &a;printf("a的地址:%p\n", &a);printf("b的地址:%p\n", &b);printf("修改前p的地址:%p\n", p);//定义二级指针int** pp = &p;*pp = &b;printf("修改后p的地址:%p\n", p);return 0;
}

        可以发现p的地址被修改了,简单点来说:初始时,p指向变量a的地址。然后,通过pp这个二级指针,修改了p的指向,使其指向了变量b的地址。因此,最后输出显示p的地址发生了变化,从指向a的地址变为指向b的地址。

4.2.2  利用二级指针获取变量中记录的数据

#include <stdio.h>int main()
{//定义变量int a = 10;int b = 20;//定义一级指针int* p = &a;//定义二级指针int** pp = &p;printf("修改前获取变量里面的数据:%d\n", **pp);*pp = &b;printf("修改后获取变量里面的数据:%d\n", **pp);return 0;
}

C语言指针·入门用法超详解-CSDN博客

指针_时光の尘的博客-CSDN博客

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com