C语言-内存管理

1.内存管理

1.1更灵活的内存管理方式

加载对应的库函数就可以申请动态的内存

对应的头文件#include<stdlib.h>

1.2 malloc

  • malloc

    • 函数原型:void *malloc(size_t size) ;

    • malloc函数向系统申请分配size个字节的内存空间,并返回一个指向这块空间的指针。

    • 返回的是void类型的函数指针,所以需要强制转换成需要的类型的指针。

#include<stdlib.h>
int main()
{
	int *ptr ;
	ptr = (int *)malloc(sizeof(int)) ; // 申请的是int类型的空间
	if(ptr == NULL)
	{
		printf("分配内存失败! \n") ;
		exit(1) ; 
	 }
	 
	 printf("请输入一个整数:") ;
	 scanf("%d",ptr) ;
	 
	 printf("你输入的整数是:%d\n",*ptr) ; 
}

1.3 free

注意:free释放完指针,指针还是在原来的位置,并不是野指针或者空指针,只是非法访问了。


 	int *ptr ;
	ptr = (int *)malloc(sizeof(int)) ; // 申请的是int类型的空间

	printf("你输入的整数是:%d\n",*ptr) ;
	free(ptr) ;
	printf("你输入的整数是:%d\n",*ptr) ; //free之后,在打印,就打印不出来了。 

2.内存泄露

申请的动态内存没有及时释放,申请了内存空间,没有释放,就会占用电脑的内存

c语言不具备垃圾回收机制

下面的这个程序会卡死,不要轻易尝试,可以在虚拟机尝试。

3.malloc还可以申请一块任意尺寸的内存空间

# ...
int main()
{
	int *ptr = NULL ;
	int num ,i ;
	printf("请输入待录入整数的个数:") ;

	scanf("%d",&num) ;

	ptr = (int *)malloc(num * sizeof(int)) ;  // 申请指定的内存空间。

	for(i = 0 ; i < num ; i++)
	{
		printf("%请录入第%d个整数:",i+1) ;
		scanf("%d",&ptr[i]) ;
	}

	printf("你录入的整数是:") ;
	for(i = 0 ; i < num ; i++)
	{
		printf("%d  ", ptr[i] ) ;
	}
	putchar('\n') ;// 换行
	free(ptr) ; // 释放内存空间。 
}

4.初始化内存空间

下面的方法可以用java的数组Arrays的内置方法做比较来理解,不过c要考虑指针,麻烦了不少。

下面的这些函数与c语言中str开头的也有所不同,str开头的是针对字符串的,这个mem开头的是用来处理内存空间的。

4.1memset

函数声明: void *memset( void *dest, int c, size_t count );

案例: 
int main()
{
	int *ptr = NULL ;
	int i ; 
	
	ptr = (int *)malloc(N * sizeof(int)) ;
//	if(ptr == NULL)    // 写上更加严谨,也可以不写这一段 
//	{
//		exit(1) ;
//	}

	memset(ptr,0,N*sizeof(int)) ; // 指向,初始化的值,内存大小。 
	
	for(i = 0 ; i < N ;i++)
	{
		printf("%d " , ptr[i] );
	 } 
	 putchar('\n') ;
	 free(ptr) ;
}

4.2 memcpy

函数声明: void *memcpy( void *dest, const void *src, size_t count );

该函数将 src 的 count 个字节复制到 dest。该函数返回 dest 的起始位置。

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[50] = { 0 };
	char* b = "csdn.com";
	memcpy(arr, b, strlen(b));
	printf("%s", arr);
	return 0;
}

4.3 memmove

函数声明: void *memmove( void *dest, const void *src, size_t count );

该函数的作用和memcpy类似。但是为什么会有memmove呢?

我们看下面这段代码

#include <stdio.h>
#include <string.h>
 
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	memcpy(arr + 3, arr, 7);
	int i = 0;
	for (i = 0; i < 10; i++)
		printf("%d ", arr[i]);
	return 0;
}

我们可能认为答案是 1 2 3 1 2 3 4 5 6 7,但是结果并不是

与memcpy函数相比,memmove函数更加灵活,因为它能够处理源内存块和目标内存块之间可能会发生重叠的情况。在处理重叠的情况时,memmove函数会先将要复制的数据拷贝到一个临时缓冲区中,然后再将数据复制到目标内存块中,从而避免数据损坏或丢失。

5.calloc

申请内存空间,并默认初始化为0,相当于malloc和memset函数的结合应用

calloc申请内存空间时,只用写一部,而malloc需要两步。

6.realloc

是C标准库中的一个动态内存分配函数,用于重新分配已经分配的内存块的大小

void *realloc(void *ptr, size_t size);

int main()
{	
	int *ptr1 = NULL ;
	int *ptr2 = NULL ;
	
	// 第一次申请的内存空间
	ptr1 = (int *)malloc(10 * sizeof(int)) ;
	 
	//进行若干操作发现ptr1申请的内存空间不够
	
	//第二次申请的内存空间
	ptr2 = (int *)malloc(20 * sizeof(int)) ;
	
	//将ptr1的数据拷贝到ptr2中
	memcpy(ptr2,ptr1,10) ;
	free(ptr1) ;
	
	//对ptr2申请的内存空间进行若干操作	。。。
	
	free(ptr2) ; 
}

int main()
{
	int i , num ;
	int cnt = 0 ;
	int *ptr = NULL ; //  注意:这里必须初始化为NULL

	do
	{
		printf("请输入一个整数(输入-1表示结束):") ;
		scanf("%d",&num) ;
		cnt ++ ;

		ptr = (int *)realloc(ptr,cnt*sizeof(int)) ;  //返回新的内存地址给ptr,可以存放最新的数据
		if(ptr == NULL) 
		{
			exit(1) ;
		}
		
		ptr[cnt-1] = num ;
	}
	while(num != -1) ;

	printf("输入的整数分别是: ") ;
	for(i = 0 ; i < cnt ; i++)
	{
		printf("%d ", ptr[i]) ;
	}
	putchar('\n') ;
	free(ptr) ; 
}

注意

该篇文章的内存管理,很多地方都可以理解为java数组的内存管理,以及初始化数组啊之类的。

7.c语言的内存布局

7.1内存布局规律

打印各种变量的地址和函数的地址,看有什么规律

int global_uninit_var ;
int global_uninit_var1 = 520  ;  //两个全局变量 
int global_uninit_var2 = 880  ;
 
 void func(void) ;  // 声明函数
 
 void func(void)
 {
 	
 }

int main(void)
{
	int local_var1 ;                  //两个局部变量  
	int local_var2 ;
	
	static int static_uninit_var ;      //两个静态变量,一个初始化,一个未初始化 
	static int static_init_var = 456;
	
	char *str1 = "I love FishC.com!" ;  // 两个字符串数组 
	char *str2 = "You are right!" ;
	 
	int *malloc_var = (int *)malloc(sizeof(int)) ;  // 申请内存空间地址
	
	printf("addr of func -> %p\n",func) ; 
	printf("addr of str1 -> %p\n",str1) ; 
	printf("addr of str2 -> %p\n",str2) ; 
	printf("addr of global_uninit_var1 -> %p\n",global_uninit_var1) ; 
	printf("addr of global_uninit_var2 -> %p\n",global_uninit_var2) ; 
	printf("addr of static_uninit_var -> %p\n",static_uninit_var) ; 
	printf("addr of static_init_var2 -> %p\n",static_init_var) ; 
	printf("addr of global_uninit_var -> %p\n",global_uninit_var) ; 
	printf("addr of malloc_var -> %p\n",malloc_var) ; 
	printf("addr of local_var1 -> %p\n",&local_var1) ; 
	printf("addr of local_var2 -> %p\n",&local_var2) ; 
	 
	
}

 

根据上图的运行结果可以发现。下面的规律。

7.1.1代码段

str1 和 str2是在代码段中的

7.1.2数据段

7.1.3 BSS段

8.堆

9.栈

10.栈和堆的区别

10.1生存周期

// 生存周期的案例

int *func(void)
{
	int *ptr = (int *)malloc(sizeof(int)) ;
	if(ptr == NULL)
	{
		exit(1) ;
	}
	*ptr = 520 ;
	
	return ptr ;
}

int main(void)
{
	int *ptr = NULL ;
	ptr = func() ;
	
	printf("%d\n",*ptr) ;  //最后输出的结果为520. 
	free(ptr) ; // 在main函数中释放内存空间,因为只有在main函数中才被使用了,因此在main函数汇总调用free函数 
				// 如果没有返回值的函数,应该在函数内部释放空间 
}

10.2发展方向

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eHgrhMu3-1683818878070)(assets/image-20230425092719720.png)]

int main(void)
{
	int *ptr1 = NULL ;
	int *ptr2 = NULL ;
	
	ptr1 = (int *)malloc(sizeof(int)) ;
	ptr2 = (int *)malloc(sizeof(int)) ;
	
	printf("stack: %p -> %p\n",&ptr1,&ptr2) ;
	printf("heap: %p -> %p\n",ptr1,ptr2) ;
	 
}

结果可以看出

  • 栈是从高地址向地址发展的

  • 堆是从低地址向高地址发展的。

11.内存池

如果一直使用malloc和free函数,申请释放内存空间的话,可能会产生内存碎片

就是申请了一片空间,再释放之后,重复多次,每个空间都没有连在一起,就成为了碎片,这样描述是便于理解,不是完全正确

为了解决这个问题,有了内存池

内存池:让程序额外维护一个缓存区域

12.位域

#include<stdio.h>

int main()
{
	struct Test
	{
		unsigned int a:1 ;
		unsigned int b:1 ;
		unsigned int c:2 ;
		
	};
	
	struct Test test ;
	test.a = 0 ;
	test.b = 1 ;
	test.c = 2 ;  // 因为二进制中的2是10,所以需要两个位置来存放,所以上面的位域写的是2 
	
	printf("a = %d, b =  %d, c = %d\n",test.a, test.b , test.c) ;
	printf("size of test  = %d\n",sizeof(test)) ; 
}

==位域的字节必须是小于数据类型的位的,例如,int是32位,所以不能超过32。

12.2无名位域

一般用来填充或者调整成员之间的位置


unsigned int a:1 ;
unsigned int b:1 ;
unsigned int c:2 ;

};

struct Test test ;
test.a = 0 ;
test.b = 1 ;
test.c = 2 ;  // 因为二进制中的2是10,所以需要两个位置来存放,所以上面的位域写的是2 

printf("a = %d, b =  %d, c = %d\n",test.a, test.b , test.c) ;
printf("size of test  = %d\n",sizeof(test)) ; 

}


[外链图片转存中...(img-C4A3sHeE-1683818878074)]

==位域的字节必须是小于数据类型的位的,例如,int是32位,所以不能超过32。

## 12.2无名位域

一般用来填充或者调整成员之间的位置

[外链图片转存中...(img-v32Fitw9-1683818878074)]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言-学生信息管理系统是一个基于链表数据结构的学生信息管理系统。链表是一种数据结构,可以存储和管理一系列具有相同类型的数据。在学生信息管理系统中,链表被用来存储和操作学生的基本信息。 该系统主要有以下功能: 1. 添加学生信息:可以添加学生的姓名、学号、性别、年龄等信息,并将该学生的信息节点插入到链表中。 2. 删除学生信息:根据学号或其他关键词查找到对应的学生信息节点,并从链表中删除该节点。 3. 修改学生信息:根据学号或其他关键词查找到对应的学生信息节点,并根据需求修改学生的信息。 4. 查询学生信息:可以根据学号或其他关键词查找到对应的学生信息节点,并显示该学生的详细信息。 5. 遍历学生信息:可以遍历整个链表,显示所有学生的基本信息。 链表的优势在于插入和删除节点的操作比较高效,因为只需要改变节点的指针指向即可,不需要移动其他节点。而数组在插入和删除操作时需要移动其他元素,效率较低。 在实现学生信息管理系统时,可以使用指针来操作链表,通过指针的指向找到链表的某个节点,并进行相应的操作。同时,需要注意对内存的管理,确保动态分配和释放内存的正确性,避免内存泄漏和访问错误。 总之,C语言-学生信息管理系统是一个基于链表数据结构的系统,可以实现学生信息的增删改查等功能。通过灵活运用链表的特点,可以高效地管理学生的基本信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值