大家好我是技术男老张,今天给大家分享的内容是“指针与内存”相关的知识内容,希望通过本文的内容能够帮助大家快速的了解和掌握“指针及内存管理”及使用。首先声明本人不是写书的专业人士,本人就是理工男一枚,写博文纯属个人兴趣 文笔水平很一般,难免会出现错别字或语法结构不通的情况,请大家多多理解。
上一篇文章介绍了【指针与函数】相关知识点,感兴趣的朋友可以点击下方连接:
还是老规矩,讲解之前我们先来 总结上一篇文章内容的知识点:
- 函数指针是一个特殊的指针,它指向一个函数。函数指针可以用来调用函数。
- 函数指针声明及定义有三种方式,分别是:传统方式、定义并初始化指针方式、 typedef 关键字定义方式。
- 函数指针可以作为某个函数的参数传递来完成函数的调用。
- 函数指针与指针函数的区别:函数指针就是一个指针,指向函数的入口地址。而指针函数就是一个函数,只是这个函数的返回类型是指针。
从今天开始,以往讲过的内容不在做详细介绍,其目的是为了推进进度,因为介绍完C语言基础后还要要讲上机实战的内容,所以大家多理解,下面介绍指针在内存中的使用及内存管理相关内容:
一、指针与内存
C语言的内存管理主要指动态内存的申请和释放。C语言没有自动的垃圾回收机制,因此内存的管理是重要的一环,如果掌握不好很容易出现内存泄漏的情况发生。
1. 动态分配内存
动态内存分配:使用 malloc, calloc, realloc 等函数进行内存的申请。接下来介绍函数的功能、区别及场景。
在讲解新内容之前我先介绍一下 viod 类型的指针 viod * ,在C语言中,void * 类型的指针称为通用指针。它可以指向任何数据类型的对象,但是不能直接对void *指针所指向的对象进行解引用操作,也不能对void *指针进行sizeof操作。
功能介绍:
malloc:用于分配指定大小的内存块,返回一个指向该内存块的指针。它接受一个参数,表示要分配的内存大小(以字节为单位)。
原型为:void * malloc(size_t size)。
calloc:用于分配可以容纳多个相同大小元素的内存块,并将分配的内存块初始化为0。它接受两个参数:一个是元素的数量,另一个是每个元素的大小。
原型为void * calloc(size_t num_elements, size_t element_size)。
realloc:用于调整之前分配的内存块的大小。它接受两个参数:一个是指向之前分配的内存块的指针,另一个是新的大小(以字节为单位)。
原型为void * realloc(void * ptr, size_t new_size)。
初始化区别:
malloc:分配的内存块内容是未初始化的,即内存块中的数据是随机的,可能包含任何值
calloc:分配的内存块会被初始化为0。
realloc:调整内存块大小时,如果新的大小大于原来的大小,那么可能会导致内存块的移动,这意味着原来内存块中的内容可能会被复制到新的位置。如果新的大小小于原来的大小,那么可能内存块的尾部部分会被截断。
使用场景:
malloc:适用于需要分配未初始化的内存块的情况。
calloc:适用于需要分配并初始化为0的内存块的情况。
realloc:适用于需要调整已有内存块大小的情况。
2. 内存释放
free函数用于释放由malloc、calloc或realloc等动态内存分配函数所分配的内存空间。 free函数的原型为:
void free(void *ptr);
其中,
ptr
是一个指向需要释放的内存块的指针。调用free函数后,之前通过动态分配函数获得的内存将被释放,并可以由系统重新分配给其他程序使用
二、使用方法
1. malloc 函数的使用
int num = 10 ; //定义分配的空间大小
int *p = (int *)malloc(10 * sizeof(int));
if(p == NULL)
{
//内存分配失败
}
else
{
//对申请的内存进行赋值操作
for(int i = 0; i < num; i++)
{
p[i] = i+10;
}
//读取内存的值
for(int i = 0; i < num; i++)
{
int value = p[i];
}
//内存使用完我们要记得释放
free(p); // 释放malloc分配的内存
}
第一步:上面代码我们先定义了一个 num变量用于申请内存的大小,
第二步:我们开始申请内存 malloc 函数要求一个参数:所需内存的字节数。我们给malloc 传入的参数为 10 * 4 = 40,就是我们申请了40个字节的内存空间,sizeof 函数作用是根据类型返回实际的字节数量,我们传入int类型后就返回4,表示4个字节。
第三步:我们判断指针p 如果等于 NULL 就说明内存申请失败,如果成功就进入else代码块。
第四步:通过for循环来给申请的内存空间赋初值,循环结束后 我们申请的内存所存储的值为 10~19。
第五步:通过fort循环来读取内存值,并赋值给value,循环结束后 value的值分别为 10 ~1 9 。
第六步:完成对内存的写入和读取后,调用free函数并传入了p指针 来释放我们申请的内存。
2. calloc 函数的使用
int num = 10 ; //定义分配的空间大小
int *p = (int *)calloc(num, sizeof(int));
if(p == NULL)
{
//内存分配失败
}
else
{
//内存分配成功
}
上面代码 calloc 函数要求两个参数:元素的数目和每个元素的字节数, 如果内存申请成功就执行 else代码块, 释放内存也是使用 free(p); 函数。
3. realloc 函数的使用
realloc
函数用来更改以前分配的内存的大小。
int num = 10 ; //定义分配的空间大小
int *p = (int *)malloc(num * sizeof(int));
int *new_p = (int *)realloc(p, sizeof(int) * 6);
if(new_p == NULL)
{
//内存分配失败!
}
else
{
//内存分配成功
}
第一步:通过 malloc函数申请内存,假设申请成功后 指针 p 就指向了这块内存空间。
第二步:我们定义了一个 new_p指针 用于指向修改后的内存地址,先使用realloc 函数来修改先前申请的内存大小,realloc 要求两个参数:指向已分配内存的指针和新的大小,我们分别传入了 p 指针及 参数6,我们期望修改后的内存大小为 24 个字节 即 4 * 6。
第三步:如果 new_p 指针为 NULL 就说明我们修改失败否则修改成功。
三、 内存拷贝与设置
在C语言中,memcpy
和memset
是常用的内存操作函数,它们分别用于内存复制和内存设置,需要注意这两个函数只能操作指针对象。
1. memcpy函数
用于复制内存内容,函数原型如下:
void *memcpy(void *dest, const void *src, size_t n);
函数参数:
dest
:目标内存区域的指针。src
:源内存区域的指针。n
:要复制的字节数。
示例代码:
char str1[50] = "Hello, World!";
char str2[50];
memcpy(str2, str1, 30);
上面的代码通过memcpy函数把 str1数组的内容拷贝到str2数组内,拷贝str1前30个字节内容到str2。
2. memset 函数
用于将一块内存中的内容全部设置为指定的值,函数原型如下:
void *memset(void *s, int c, size_t n);
函数参数:
- s:要填充的内存区域的指针。
- c:设置的值。
- n:要设置的字节数。
示例代码:
char str[50];
memset(str, 'a', sizeof(str));
上面的代码通过memset函数把 字符 a,写入到str数组内,写入字节数是50个字节,这时str数组内的元素值都是 字符 a。
四、浅拷贝与深拷贝
深浅拷贝实际上是人为定义的概念,深浅拷贝简单理解:浅拷贝就是通过指针方式指向一个对象然后通过指针方式来进行数据拷贝,没有开辟新的内存空间。而深拷贝是拷贝一个对象的副本,需要开辟一个新的内存空间来完成数据的拷贝。什么情况下需要深拷贝?比如:不想破坏原对象及数据的情况下可以采用深拷贝,或者指针指向了这个对象后释放当前指针时不希望原始对象也随之释放时可以使用深拷贝,其实C语言本无深浅拷贝之分,如果真正的了解指针就会发现,所谓的深浅拷贝都是概念而已。
特别说明:memcpy
本身不涉及任何所谓的"深拷贝"或"浅拷贝"概念,它仅进行值拷贝,不涉及对象的拷贝。
好了,今天内容的分享就到这里,有不理解的朋友可以在底部留言,下一篇文章的内容依然是”指针“的相关内容 。感谢大家的支持!