目录
为什么会存在动态内存分配:
malloc:
free:
calloc:
realloc:
注意事项:
攻破经典易错题:
题目一:
存在以下两种方式进行修改:
1.利用二级指针进行修改:
2.利用返回指针的方式进行修改:
题目二:
题目三:
题目四:
柔性数组:
柔性数组的优势:
总结:
为什么会存在动态内存分配:
动态内存分配可以提高程序的灵活性和效率。使用动态内存分配可以在程序运行时根据需要分配和释放内存空间,而不是在程序编译时分配固定的内存空间。这样可以避免浪费内存空间和提高内存利用率。动态内存分配还可以支持动态数据结构,例如链表和树等,这些数据结构的大小不确定,可以在运行时根据实际需要调整大小。此外,动态内存分配还可以避免栈溢出和内存泄漏等问题。因此,动态内存分配在编写大型程序时非常有用。
目前我们学习有2种申请内存的方式
int a = 10;
int arr[] = {1, 2, 3, 4, 5};
通过上述两种方法实现的内存申请,申请到的内存空间固定,不够灵活。
接下来我们就来介绍介绍实现动态内存开辟的一系列函数:
malloc:
malloc()函数是C语言中用于分配动态内存的函数,具有以下原型:
void *malloc(size_t size);
该函数返回一个指向分配内存的指针,如果没有足够的内存空间,则返回NULL。参数size表示要分配的内存大小(以字节为单位),可以是任意整数或表达式。
使用malloc()函数时,需要注意以下几点:
- malloc()函数返回的指针指向的是一块连续的、未初始化的内存空间,可以通过类型转换将其转换为所需类型的指针。
- 在使用malloc()函数申请内存后,需要在不需要使用该内存时将其释放掉,否则会造成内存泄漏。
- 释放内存时使用free()函数,其原型为:
void free(void *ptr);
其中ptr是之前使用malloc()函数返回的指针,如果该指针为NULL,则free()函数不执行任何操作。
下面是一个使用malloc()函数分配和释放内存的示例代码:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p;
p = (int*)malloc(sizeof(int) * 10); // 分配10个整数的内存空间
if (p == NULL) {
printf("Error: failed to allocate memory.\n");
return 1;
}
for (int i = 0; i < 10; i++) {
*(p + i) = i; // 对内存空间进行赋值
}
for (int i = 0; i < 10; i++) {
printf("%d ", *(p + i)); // 输出内存空间的值
}
free(p); // 释放内存空间
return 0;
}
在我们释放malloc开辟的空间时,我们存在通过free来释放——主动释放
也有通过程序退出后,malloc申请的空间也会被操作系统收回——被动释放。
因此为了安全起见,我们最好首选主动释放。
free:
free函数是一种用于释放在堆上分配的内存块的函数。该函数接受一个指向已分配内存块的指针作为参数,并将该内存块的使用计数减少1。如果减少后计数为0,则释放该内存块。这个函数是C库函数,定义在stdlib.h头文件中。在使用动态内存分配函数malloc或calloc分配内存后,必须使用free函数来释放内存,以避免内存泄漏。
如果我们需要释放掉一块我们自己开辟出来的空间,
我们可以进行一下操作:
free(p);
p = NULL;
要注意的是,free(p)之后的p = NULL必不可少!!!
calloc:
calloc() 函数是 C 语言标准库中的函数,用于动态分配内存空间,并将内存空间的每个字节初始化为0。
calloc() 函数的声明如下:
void* calloc(size_t num, size_t size);
其中,第一个参数 num 表示需要分配的元素个数,第二个参数 size 表示每个元素的大小。calloc() 函数返回指向分配的内存空间的指针,如果分配失败则返回 NULL。
calloc() 函数与 malloc() 函数类似,但是 calloc() 在分配内存空间时会将空间中的每个字节都初始化为0,而 malloc() 函数不会做这个操作。因此,如果需要使用一块全为0的内存空间,可以使用 calloc() 函数来分配内存。
使用 calloc() 函数的例子如下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr;
int i, n;
printf("Enter number of elements: ");
scanf("%d", &n);
ptr = (int*)calloc(n, sizeof(int));
if (ptr == NULL)
{
printf("Memory not allocated.\n");
exit(0);
}
else
{
printf("Memory successfully allocated using calloc.\n");
for (i = 0; i < n; ++i)
{
printf("Enter element %d: ", i+1);
scanf("%d", &ptr[i]);
}
printf("The elements you entered are: ");
for (i = 0; i < n; ++i)
{
printf("%d ", ptr[i]);
}
}
free(ptr);
return 0;
}
在上述示例中,我们使用 calloc() 函数来分配一段大小为 n * sizeof(int) 的内存空间,并将其赋值给 ptr 指针。然后,我们输入 n 个整数,将其存储到分配的内存空间中,并输出这些整数,最后释放已分配的内存空间。
realloc:
C语言中的realloc函数是动态内存分配函数之一,其功能是用于重新分配已分配内存的大小,实现动态内存管理。
函数定义:
void *realloc(void *ptr, size_t size);
参数说明:
- ptr:需要重新分配内存的指针地址。
- size:重新分配的内存大小(字节长度)。
函数返回值:
- 若重新分配成功,则返回新分配内存的指针,且原有指针指向的内存已经被释放;
- 若重新分配失败,则返回NULL,原有指针所指向的内存不会被释放。
使用realloc函数时需要注意以下几点:
- 如果ptr为NULL,则realloc的操作效果等价于malloc(size),即它将分配一个新的内存块并返回其指针;
- 如果size为0,则realloc的操作效果等价于free(ptr),即它将释放原有的内存块;
- 如果ptr不为NULL且size不为0,则realloc的操作效果是将原有内存块中的数据载入新分配的内存块中,原有内存块将被释放。
- 如果realloc返回NULL,则说明重新分配内存失败,应该停止程序运行并进行相关的错误处理。
使用realloc函数时应该注意安全性和合理性,避免出现内存泄漏、内存重叠等问题。
这里要注意的一点是:
我们在实现realloc开辟空间时,要清楚的知道realloc开辟空间也会失败,失败则会返回NULL,
所以我们应当创建一个临时指针变量,并作出判断再讲临时指针变量赋值到原指针处。
如果我们在使用realloc的时候,遇到“后面的空间被占用了”这种情况时。
此时realloc函数就会去找一块新的并且足够的空间,一次性将空间直接开辟好。
并将旧空间里的数据直接拷贝到新的空间,并且释放掉旧空间的数据和空间本身,
再返回新的空间的地址。
注意事项:
在我们使用上述所讲的函数时,要注意一下:
1.注意千万不要对NULL直接进行解引用操作。
2.注意一定不要越界访问。
3.注意不可以仅仅使用free释放掉一部分。
4.对同一块空间多次释放。
5.不可以不释放空间。
6.注意不可以对非动态内存进行释放。
攻破经典易错题:
题目一:
//T1
void GetMemory(char* p)
{
p = (char*)malloc(100);
}
void test(void)
{
char* str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
以上代码存在以下问题:
1.未对str进行free释放。
2.存在内存泄漏。
3.程序对NULL直接进行解引用操作,程序崩溃!
存在以下两种方式进行修改:
1.利用二级指针进行修改:
//修改:
void GetMemory(char**p)
{
*p = (char*)malloc(100);
}
void test(void)
{
char* str = NULL;
GetMemory(&str);
strcpy(str, "hello world");
printf(str);
//释放str开辟的空间
free(str);
str = NULL;
}
2.利用返回指针的方式进行修改:
char* GetMemory(char* p)
{
p = (char*)malloc(100);
return p;
}
void test(void)
{
char* str = NULL;
str = GetMemory(str);
strcpy(str, "hello world");
printf(str);
//释放str开辟的空间
free(str);
str = NULL;
}
以上的输出结果都为:
题目二:
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
以上存在以下问题:
函数吊桶结束后,str变为野指针。
此时str指向的空间已被释放
那么此时返回栈空间出现问题!
题目三:
void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
void tests(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
以上存在的问题主要未使用free进行释放,
因此存在内存泄漏的问题。
题目四:
//题目四
void test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL)
{
strcat(str, "world");
printf(str);
}
}
这里存在的问题主要是
1.对内存的非法访问。
2.free完后未将str设为NULL。
修改后应当如下:
//题目四
void test(void)
{
char* str = (char*)malloc(100);
if (str != NULL)
{
strcpy(str, "hello");
strcat(str, "world");
printf(str);
}
else
{
perror("str->malloc");
return;
}
free(str);
str = NULL;
}
柔性数组:
C语言中的柔性数组指的是一种特殊的数组,它可以在定义时不指定数组大小,在运行时动态分配内存。它的定义格式为:
struct my_struct {
int size;
int data[]; // 柔性数组
};
在这个定义中,data[]就是一个柔性数组,它没有指定大小,因此可以在运行时根据需要动态分配内存。注意,在柔性数组后面不能再有其他成员。
在使用柔性数组时,我们可以通过以下方式进行动态内存分配:
struct my_struct *p = malloc(sizeof(struct my_struct) + n * sizeof(int));
p->size = n;
这里我们通过malloc函数动态分配了一个结构体my_struct和一个大小为n的int数组,p指向结构体的首地址。由于柔性数组不占据结构体内存空间,因此可以使用sizeof(struct my_struct) + n * sizeof(int)来计算需要分配的内存大小。通过p->size来记录数组的大小。
在使用柔性数组时,我们可以像普通数组一样访问其中的元素,例如:
p->data[0] = 1;
p->data[1] = 2;
p->data[2] = 3;
需要注意的是,柔性数组只能用于结构体的最后一个成员,而且在定义时必须省略数组大小。同时,在使用柔性数组时,需要注意内存对齐问题,否则可能会导致程序出错。
当空间不够时,可以使用以下代码:
struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 40);
if(ptr != NULL)
{
ps = ptr;
}
else
{
perror("relloc");
return 1;
}
柔性数组的优势:
-
更灵活的内存管理:柔性数组始终分配恰好足够的内存空间,不会浪费内存。同时,由于可以在运行时动态分配内存空间,可以避免在编译时限制数组长度。
-
更高效的代码:使用柔性数组可以减少代码中的重复代码和计算,因为只需要计算数组的实际长度一次,并且可以像普通数组一样方便地访问和操作。
-
更方便的代码编写:使用柔性数组可以使代码更加简洁和易于理解,因为不需要手动处理内存分配和释放,也不需要手动处理数组长度。
总的来说,柔性数组是一种方便、高效和灵活的内存管理工具,特别适用于需要动态分配数组空间的情况。
总结:
以上就是关于动态内存的内容了,学习完后下来可以自己动手在VS中使用动态内存创建创建空间,熟练使用它们。
在接下来的blog中我们将会实现通讯录的创建运用动态内存管理。
所以在此基础上,我们最好可以进行复习结构体和动态内存的相关知识。
记住:
“坐而言不如起而行”
Action speak louder than words!
以下是我的Gitee:
dynamic_memory_question_CSDN/dynamic_memory_question_CSDN/test.c · 无双/test_c_with_X1 - Gitee.com