指针详细讲解(2)

本文详细介绍了C语言中野指针的成因,如未初始化、空间释放后使用和越界访问,以及如何通过初始化、正确处理和使用assert避免这些问题。此外,还探讨了一维数组名的含义、sizeof操作符的应用以及数组作为参数传递的本质。
摘要由CSDN通过智能技术生成

目录

1.野指针

1.1野指针的成因

1.1.1初始化指针时没有给出明确地址

1.1.2指针所指向空间已被释放

1.1.3指针的越界访问

1.2如何避免空指针

1.2.1初始化有明确去向

1.2.2当用完此指针变量时,也要置为NULL空指针

1.2.3assert断言

2.指针在一维数组中的理解

2.1一维数组名的理解

2.2数组名的两个特殊搭配

2.2.1sizeof(数组名)

2.2.2&数组名

2.3一维数组传参的本质

3.感谢查阅


1.野指针

野指针指的是,所使用得到指针指向的是未知的、随机的、不正确的,这样的指针被使用后很容易造成越界访问。

1.1野指针的成因

1.1.1初始化指针时没有给出明确地址

int* a ;

若写出这样的指针,那么这就是个野指针。在初始化时,并没有把此指针置为空指针NULL;

也没有指向某个明确的地址。

1.1.2指针所指向空间已被释放

int* test()
{
int a = 100; // 在这里创建了变量a,并且这个函数返回的值就是这个a的地址
return &a;
}

int main()
{
int *ptr = test(); // 这里用ptr指针变量来接受test函数返回的地址
return 0;
}

可以通过这段代码来解释这个错误:

当在函数中创建变量a后,a的地址中确实存在着100这个数,但是当test函数运行完后,变量a被系统回收,那么这个地址内就不再存在着100,可能指向着任何内容,此时把这个地址返回给主函数中的ptr,这是一个很危险的行为。

1.1.3指针的越界访问

#include<stdio.h>
int main()
{
	int arr[5] = { 0 };
	int* p = &arr;
	for (int i = 0; i < 6; i++)
	{
		*(p + i) = i;
	}
	return 0;
}

通过这段代码可以发现,创建的数组只有5个整型单位的长度,但是在for循环中,i的循环次数来到了6次,这样就会让p越界访问,超过了数组的长度。


1.2如何避免空指针

1.2.1初始化有明确去向

当在创建指针变量时,明确地将此指针指向自己所需要的空间,若暂时用不到,务必要将其置为NULL空指针。

1.2.2当用完此指针变量时,也要置为NULL空指针

当运用完一个指针变量时,即时把此指针置为NULL空指针,以防影响后续的指针运用。

1.2.3assert断言

在C语言中,有语句assert,需要带有头文件<assert.h>

int * a = NULL;
assert(a);

当在代码中使用assert断言时,系统会辨别assert内表达式的真假,如果为真,那么就继续运行,如果为假,那么在编译运行时就会报错并且指出位置。

我们可以通过这个语句,在每次使用指针前来确定此指针是否为空指针。


2.指针在一维数组中的理解

2.1一维数组名的理解

数组名代表着什么?

int main()
{
	int arr[10] = { 1 };
	int* s = &arr[0];
	int* sl = arr;
	printf("%d \n", *s);
	printf("%d \n", *sl);

	return 0;
}

可以通过运行结果看出,两个指针变量指向的分别是,&arr[0] 和 arr , 但打印出的是同结果,那么我们是否可以推测:数组名就代表着数组中首元素的地址呢?

我们可以通过调试来证实一下:

证实了我们的猜测是正确的。


2.2数组名的两个特殊搭配

2.2.1sizeof(数组名)

当使用sizeof(数组名)时,此时的数组名代表着什么呢? 

int main()
{
	int arr[10] = { 1 ,2,3,4,5,6,7,8 };
	int a = sizeof(arr);
	printf("%d", a);

	return 0;
}

可以看出此时sizeof算出的是整个数组中总元素的总大小,所以此时的数组名代表着的就是整个数组。

2.2.2&数组名

当对数组名取地址时,取到的地址是什么呢?

从这张图看出似乎还是首元素的地址,但此地址并非首元素的地址。

此时的地址其实是整个数组的地址,但是这个地址的起始处也是首元素,所以才会和首元素地址相同。这两个地址虽然地址相同,但是还是有区别的。

这里我们通过地址+整数来体现他们的区别:

int main()
{
	int arr[10] = { 1 ,2,3,4,5,6,7,8 };
	int* a = &arr;
	int* b = &arr+1;
	int* c = &arr[0];
	int* d = &arr[0]+1;

	printf("%p\n", a);
	printf("%p\n", b);
	printf("%p\n", c);
	printf("%p\n", d);

	return 0;
}

可以看出,对数组名取地址再+1,地址向后走了8个int的长度,但是对首元素地址+1,地址向后走了一个int的长度。

2.3一维数组传参的本质

我们已经知道了一维数组名代表的就是首元素的地址,那么我们在对数组进行传参的时候,可以直接传数组名也可以传首元素的地址,都是可行的。

#include <stdio.h>
void test(int arr[])
{
 int sz2 = sizeof(arr)/sizeof(arr[0]);
 printf("sz2 = %d\n", sz2);
}
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 int sz1 = sizeof(arr)/sizeof(arr[0]);
 printf("sz1 = %d\n", sz1);
 test(arr);
 return 0;
}

3.感谢查阅

会继续进行更新,感谢大家的阅读。

C++指针是一种非常重要的概念,它是一种用于存储内存地址的变量类型,可以用于直接访问内存中的数据。指针的基本概念需要理解以下几个方面: 1. 指针的定义和声明:指针的定义需要指定指针的类型,它可以指向任何数据类型,包括整数、字符、浮点数、结构体等。例如,int *p; 声明了一个指向整数类型的指针变量p。 2. 指针的赋值:指针变量可以通过赋值来指向某个变量的地址,例如,int a = 10; int *p = &a; 将指针变量p指向变量a的地址。 3. 指针的解引用:指针变量可以通过解引用(*)操作符来访问它所指向的变量的值,例如,*p = 20; 修改了指针变量p所指向的变量a的值。 4. 指针的算术运算:指针变量可以进行加、减等算术运算,例如,p++; 将指针变量p指向下一个整数类型的地址。 5. 指针和数组:数组名本质上是一个指向数组首元素的指针,可以通过指针和数组名来访问数组中的元素,例如,int a[5] = {1, 2, 3, 4, 5}; int *p = a; cout << *p << endl; 输出数组a的第一个元素1。 6. 指针和函数:指针可以作为函数的参数来传递变量的地址,从而在函数内部对变量进行修改,例如,void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } 可以通过调用swap(&a, &b)来交换a和b的值。 需要注意的是,指针在使用时需要注意指针变量的值是否为空,避免出现野指针的情况,同时需要注意指针变量的生命周期,避免指针变量指向的内存已经被释放的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值