BSP Day 19

本文介绍了二级指针的概念,通过示例解释了如何使用二级指针存储一级指针的地址。接着,讨论了C语言中的内存管理函数,包括memset用于初始化内存,calloc等效于malloc和memset的组合,以及realloc函数用于动态调整内存大小。文章还涵盖了realloc在内存不足时的操作,并强调了正确释放内存的重要性。最后,列举了一些关于指针和数组地址的易混淆点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二级指针

二级指针的概念:用来存放一级指针地址的指针 ,也就是指向指针的指针;
理解:我们通常所讲的指针都是指一级指针,一级指针变量用来存放普通变量的地址;当然,每个变量都有自己本身所对应的地址,一级指针也有自己的地址,我们便用二级指针变量来存储一级指针的地址;

int a = 8;//普通整型变量a
int *p1 = &a;//一级指针p1,指向整型变量a,存放的是变量a的地址
int **p2 = &p1;//二级指针p2,指向一级指针p1,存放的是指针p1的地址

 我们来看这样一段代码

int a = 8;
int *p1 = &a;
int **p2 = &p1;

printf("a = %d\n",a);//打印a的值
printf("*p1 = %d\n",*p1);//*p等价于a,打印a的值

printf("&a = %p\n",&a);//打印a的地址
printf("p1 = %p\n",p1);//打印p1的值,即a的地址

printf("&p1 = %p\n",&p1);//打印p1的地址
printf("p2 = %p\n",p2);//打印p2的值,即p1的地址

printf("*p2 = %p\n",*p2);//p2一次解引用,*p2等价于打印p1的值,即a的地址
printf("**p2 = %d\n",**p2);//p2二次解引用,**p2等价于打印a的值

运行结果如下:

 由此可得:

*p2 = p1 = &a ;
**p2 = *p1 = a;

memset函数

memset函数为初始化函数,可以将一段连续的内存初始化为某个值。
但它是以字节为单位进行初始化的。
memset函数的一般使用格式为

memset(首地址,值,sizeof(地址总大小));

 

     int *p;
	//用malloc申请一个存10个int型元素的数组 
	p = (int *)malloc(sizeof(int )*10);
	if(p==NULL)
	{
		printf("error\n");
	 } 
	 memset(p,0,40);//memset函数对申请的内存空间进行初始化 

calloc函数

内存的分配除了用malloc()函数外,还可以使用calloc()函数。

我们来看这样一段代码

#include<stdio.h>
#include<stdlib.h> 
int main()
{
	int *p;
	//calloc()函数等价于malloc()和memset() 
	p = (int *)calloc(8888889,sizeof(int));
	if(p==NULL)
	{
		printf("error\n");
	}
	int i;
	for(i=0;i<888889;i++)
	{
		p[i] = i;
		printf("%d\n",p[i]);
	 } 
	 free(p);
	 p = NULL;
	return 0;
 } 

 可以看到calloc()函数等价于memset(),同样可以开辟出一片空间。

 realloc函数

void* realloc(void* memblock, size_t size)

有时我们觉得我们用malloc​​​​​​,calloc函数申请的动态内存空间太大了,有时觉得申请的空间太小了,为了合理使用内存,我们要对内存的大小做灵活的调整,那么realloc函数就可以做到控制动态内存开辟的大小。

我们来看这样一段代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
	int* p=(int*)malloc(20);
	int i;
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
	}
	else
	{
		for (i = 0; i < 5; i++)
		{
			*(p + i) = i;
		}
		for (i = 0; i < 5; i++)
		{
			printf("%d ", *(p + i));
		}
	}
	//假设这里20个字节的空间已经不能满足我们使用了
	//我们需要40个字节的空间
	//这里就可以使用realloc调整动态开辟的内存
	int* ptr = realloc(p, 40);
	//开辟成功
	if (ptr != NULL)
	{
		p = ptr;
		for (i = 5; i < 10; i++)
		{
			*(p + i) = i;
		}
		for (i = 5; i < 10; i++)
		{
			printf("%d ", *(p + i));
		}
	}

	//考虑开辟失败的情况
	//若调整开辟40个字节失败,我们不能让原有的20个字节的空间的数据丢失
	//所以我们至少要返回原动态内存的起始地址,打印0 12 3 4
	//注意用完之后把动态内存释放
	free(p);
	p = NULL;
	
	
}

如果p指向的空间之后有足够的空间可以追加,则直接追加,返回的是p原来的起始地址。

如果p指向的空间之后没有足够的空间可以追加,则realloc函数会重新找一个新的内存区域,重新开辟一块40个字节的动态内存空间,并且把原来内存空间的数据拷贝回来,释放旧的内存空间还给操作系统,最后返回新开辟的内存空间的起始地址。

我们需要用一个新的指针变量来接收realloc的返回值。

同时我们要考虑调整内存大小失败的情况,如果开辟失败,我们至少不能让原内存数据失效,我们也要释放原内存数据,并把指针置为空。

开辟成功,也不用担心原来的内存有没有浪费,因为realloc函数会把原来的内存空间拷贝回来,再将其内存释放。注意如果ptr!=NULL p=ptr;这个p指针已经被赋为ptr了(就不用考虑原内存空间的指针有没有被置为空指针),所以新空间不再使用时,也要释放内存,并把指针置为空。

realloc函数具有与malloc函数相同的功能。

最后附上一些容易记混的东西

1.  a是一维数组名,a+1,往后挪了一个元素的地址,a是一级指针,挪一级指针挪了一个元素
2. a是二维数组名,a+1往后挪了一行元素的地址,a是二级指针,挪二级指针挪了一行元素
3.  a是二维数组名,&a+1往后挪了整个数组的地址,a是二级指针,&a是三级指针,挪三级指针挪了整个数组

4.计算sizeof(二维数组名),计算的是整个数组占的空间大小,
5. 计算sizeof(一维数组名), 计算的是一维数组占的空间大小
6. 计算sizeof(二维数组的行地址),计算的是二维数组中某行占空间大小
7. 计算sizeof(无论什么地址),计算的是地址占空间大小,与系统位数有关64位系统是8个字节,32位系统是4个字节。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixiaxiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值