【C语言】动态内存分配malloc,realloc等函数使用和常见错误(上)

简介: 【C语言】动态内存分配malloc,realloc等函数使用和常见错误(上)

本章重点:

介绍malloc ,calloc , realloc 等动态内存分配函数的使用方法与常见的动态内存错误,与讲解几道动态内存分配的笔试题


1. 为什么存在动态内存分配



int val = 20;         在栈空间上开辟四个字节
char arr[10] = {0};  在栈空间上开辟10个字节的连续空间


以上的内存开辟空间都是在栈上开辟的,而栈区的空间有以下几个特点:


  1. 空间开辟大小是固定的。
  2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
  3. 栈区由编译器自动分配释放,由操作系统自动管理,无须手动管理。
  4. 栈区上的内容只在函数范围内存在,当函数运行结束,这些内容也会自动被销毁。
  5. 栈区上的地址是由高到底的


但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。这时候就存在动态内存开辟了。

补充:

  1. 动态内存分配的空间是在堆区上开辟的
  2. 栈区上放的是局部变量和函数的形式参数等
  3. 静态区上放的是全局变量与静态变量(static)


2. 动态内存函数



malloc 和 free


malloc和free是成双成对出现的

malloc和free都声明在 stdlib.h 头文件中。


C语言提供了一个动态内存开辟的函数


void* malloc (size_t size);(size_t无符号整形)


malloc 使用方法与注意事项:

  • malloc这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
  • 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使

用者自己来决定。

  • 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。
  • 如果没有free回收空间,系统会到最后自动回收,但是中间如果有大量程序要执行,这一

块空间就一直占用着,(千万不要这样处理)不释放会产生内存碎片,小型程序可以不关注,但是在中大型程序上影响极其深刻


注意:申请的空间刚开始都放的是随机值,如果要参与运算,都要给一个初始值,否则运算的结果肯定是随机值


free函数用来释放动态开辟的内存。

void free (void* ptr);


free使用方法与注意事项:

  • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数 ptr 是NULL指针,则函数什么事都不做。
  • 每次动态内存开辟后,都要记得释放该开辟的内存
  • 释放空间后该指针还是指向原来的地址,为防止后面使用这个危险指针,应该释放完后赋

个NULL


  • 使用案例


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
  //向内存申请10个整形的空间
  int* p = (int*)malloc(40);
  //如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
  if (p == NULL)
  {
    //查询那里错误
    printf("%s\n", strerror(errno));
  }
  //如果开辟成功,则返回一个指向开辟好空间的指针
  else
  {
    //正常使用空间
    int i = 0;
    for (i = 0; i < 10; i++)
    {
      *(p + i) = i;
    }
    for (i = 0; i < 10; i++)
    {
      printf("%d\n", *(p + i));
    }
  }
  //当动态申请的空间不再使用的时候
  //就应该还给操作系统
  free(p);
  p = NULL;
  //释放空间后p还是指向原来的地址,为防止后面使用这个危险指针,使用赋个NULL
  system("pause");
  return 0;
}


最终输出结果:

0

1

2

3

4

5

6

7

8

9


calloc


C语言还提供了一个函数叫 calloc , calloc 函数也用来动态内存分配。


void* calloc (size_t num, size_t size);


函数的功能与malloc相似

  • 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化

为0。

  • 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为

全0。


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
  //calloc 全部初始化为0
  int* p = (int*)calloc(5, sizeof(int));
  if (p == NULL)
  {
    查询那里错误
    printf("%s\n", strerror(errno));
  }
  else
  {
    //正常使用
      int i = 0;
    for (i = 0; i < 5; i++)
    {
    printf("%d\n", *(p + i));
    }
  }
  当动态申请的空间不再使用的时候
  就应该还给操作系统
  free(p);
  p = NULL;//释放空间后p还是指向原来的地址,为防止后面使用这个危险指针,使用赋个NULL
  system("pause");
  return 0;
}


最终输出结果:

0

0

0

0

0


所以我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务。


calloc 和 malloc 的对比:

  1. 参数不一样
  2. 都是在堆区上申请内存空间,但是malloc不初始化,calloc会初始化为0
  3. 如果要初始化,就使用calloc不需要初始化,就可以使用malloc
目录
相关文章
|
18天前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
30 3
|
3天前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
11 3
|
19天前
|
编译器 程序员 C语言
深入C语言:动态内存管理魔法
深入C语言:动态内存管理魔法
|
5天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
9 0
|
15天前
|
C语言
保姆级教学 - C语言 之 动态内存管理
保姆级教学 - C语言 之 动态内存管理
13 0
|
9天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
28 10
|
2天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
8天前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
32 7
|
8天前
|
存储 编译器 程序员
【c语言】函数
本文介绍了C语言中函数的基本概念,包括库函数和自定义函数的定义、使用及示例。库函数如`printf`和`scanf`,通过包含相应的头文件即可使用。自定义函数需指定返回类型、函数名、形式参数等。文中还探讨了函数的调用、形参与实参的区别、return语句的用法、函数嵌套调用、链式访问以及static关键字对变量和函数的影响,强调了static如何改变变量的生命周期和作用域,以及函数的可见性。
22 4
|
13天前
|
存储 编译器 C语言
C语言函数的定义与函数的声明的区别
C语言中,函数的定义包含函数的实现,即具体执行的代码块;而函数的声明仅描述函数的名称、返回类型和参数列表,用于告知编译器函数的存在,但不包含实现细节。声明通常放在头文件中,定义则在源文件中。