文章目录
通过前面的介绍
C语言指针详解(一)超详细~
相信大家对指针的基本概念及用法有了初步的了解。
我们来回顾一下上次那个博客讲了什么吧~
1.指针就是变量,用于存放地址的,地址唯一标识的一块内存空间。
2.指针的大小分别是4/8个字节(32位平台/64位平台)
3.指针是有类型的,指针的类型决定了指针±整数的步长,以及指针解引用的权限有多大。
4.指针的运算。
那么这次博主给大家继续深入理解指针的其他高级用法吧
这是本次我们要讲解的知识点:

1.野指针
1.1 什么是野指针
1.2 造成野指针的原因有哪些呢
1.2.1造成野指针具体代码实例:
1.指针未被初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
2.指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = &arr[0];
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
3.指针指向的空间释放
int* test()//由于返回的是n的地址,因此函数返回的是int*类型
{
int n = 100;//在test函数中创建了局部变量n,
return &n;//当我们在中间的函数做了一些事情后,我们就返回n,把n的地址返回到指针变量p来接收
}
int main()
{
int* p = test();//由于返回的是地址,所以我们拿指针变量p来接收
printf("%d\n", *p);
return 0;
}
1.3 如何避免野指针呢?
1.3.1如何对指针进行初始化?
初始化如下:
include <stdio.h>
int main()
{
int num = 10;
int*p1 = #
int*p2 = NULL;
return 0;
}
1.3.2如何才能小心指针越界?
1.3.3 指针变量不再使用时,如何及时置NULL,在指针使用之前检查有效性?
2.assert断言
2.1 什么是assert断言
2.2 如何使用assert断言呢?
assert(p != NULL);
2.3 使用assert有什么好处呢?
需要注意的是,assert()也是有缺点的。由于引入了额外的检查,会增加程序的运行时间。
3.指针的使用和传址调用
3.1 学习指针的目的是什么?
经过一番思考后,我们可能会写出这个代码出来~
#include <stdio.h>
void Swap1(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d b=%d\n", a, b);
Swap1(a, b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
当我们运行此代码,结果如下:
因此我们得出以下结论:
那我们怎么解决呢?
我们得借助函数间传址调用来解决。
3.2 什么是传址调用?
3.3 怎么进行传址调用?
代码实现如下:
#include <stdio.h>
void Swap2(int*px, int*py)
{
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d b=%d\n", a, b);
Swap1(&a, &b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
运行结果如下:
4.数组名的理解
在上一次博客C语言指针详解(一)超详细~
我们曾写过两行代码:
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int *p =&arr[0];
这里我们是使用&arr[0]的方式拿到了数组第一个元素的地址。但是数组名本来就是地址,不信我们可以拿VS编译器来测试一下。
因此我们可以得出这个结论:数组名是数组首元素(第一个元素)的地址。
但是呢,有同学会有疑问,如果数组名是数组首元素的地址,那这个代码该怎么理解?
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", sizeof(arr));
return 0;
}
从下图,我们发现输出结果是40。
为什么不是4/8呢?如果数组是首元素的地址,按理说输出的应该是4/8才对。
除此之外,其他地方使用数组名,数组名都表示首元素的地址。
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);
printf("arr = %p\n", arr);
printf("&arr = %p\n", &arr);
return 0;
}
发现这三个打印的结果都一样,会再次出现疑惑?
那接下来我来介绍他们之间的区别。
4.1 arr和&arr的区别
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);
printf("&arr[0]+1 = %p\n", &arr[0]+1);
printf("arr = %p\n", arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr = %p\n", &arr);
printf("&arr+1 = %p\n", &arr+1);
return 0;
}
运行结果:
相信到这里大家应该搞清楚数组名的意义了吧。
除了有两个例外,其他的数组名都是数组首元素的地址。
5.二级指针
5.1 什么是二级指针
5.2 指针变量的地址存放在哪里呢?
另外,这里有个小细节需要大家注意的是,由于pa中的p左边的*代表pa是个指针变量,而前面的int代表pa是个int类型的指针变量。那同理,ppa中的p左边的 *代表ppa是个指针变量,而旁边还有一个 *。代表的是ppa是一个int *类型的指针变量。
5.3 对于二级指针的运算是怎么样的呢?
#include <stdio.h>
int main() {
int a = 10;
int* p = &a;//p是一级指针
int** pp = &p;//pp是二级指针
printf("%d\n", **pp);
return 0;
}
VS运行结果如下所示:
6.指针数组
6.1 什么是指针数组呢?
并且指针数组的每个元素都是用来存放地址(指针)的。
如下图所示:
7.指针数组模拟二维数组
#include <stdio.h>
int main()
{
int arr1[] = {1,2,3,4,5};
int arr2[] = {2,3,4,5,6};
int arr3[] = {3,4,5,6,7};
//数组名是数组首元素的地址,类型是int*的,就可以存放在parr数组中
int* parr[3] = {arr1, arr2, arr3};
int i = 0;
int j = 0;
for(i=0; i<3; i++)
{
for(j=0; j<5; j++)
{
printf("%d ", parr[i][j]);
//parr[i][j]==*(*(parr+i)+j))
}
printf("\n");
}
return 0;
}
** 好啦!今天博主就分享到这里**
** 如果觉得博主讲得不错的话。欢迎大家一键三连支持一下**