【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
目录
相关文章
|
1月前
|
程序员 C语言 开发者
pymalloc 和系统的 malloc 有什么区别
pymalloc 和系统的 malloc 有什么区别
|
10天前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
28 6
|
28天前
|
程序员 C语言 开发者
pymalloc 和系统的 malloc 有什么区别?
pymalloc 和系统的 malloc 有什么区别?
|
1月前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
45 3
|
2月前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
30 0
|
2月前
|
C语言
保姆级教学 - C语言 之 动态内存管理
保姆级教学 - C语言 之 动态内存管理
20 0
|
2月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
36 3
|
6天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
28 6
|
23天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
31 6
|
2月前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
43 10