为什么,或者何时,您需要在C中动态分配内存?[英] Why, or when, do you need to dynamically allocate memory in C?

本文是小编为大家收集整理的关于为什么,或者何时,您需要在C中动态分配内存?的处理方法,想解了为什么,或者何时,您需要在C中动态分配内存?的问题怎么解决?为什么,或者何时,您需要在C中动态分配内存?问题的解决办法?那么可以参考本文帮助大家快速定位并解决问题。

问题描述

动态内存分配是C编程中的一个非常重要的主题.但是,我一直无法找到它使我们能做的事情的良好解释,或者为什么需要它.

我们不能只是声明变量和结构,而不是必须使用malloc()?

作为侧面笔记,什么是差异:

ptr_one = (int *)malloc(sizeof(int));

int *ptr_one = malloc(sizeof(int));

推荐答案

需要使用动态内存,何时:

  • 您无法在编译时确定要使用的最大内存量;
  • 要分配一个非常大对象;
  • 您想要构建数据结构(容器)而无需固定的上尺寸;

您并不总是知道您需要在编译时留出多少内存.想象一下,处理数据文件(一个时间序列的温度,比如说),其中文件中的记录数不是固定的.您可能只需10条记录或多达10000次.如果要将所有数据读入内存以处理它,您将不知道在阅读文件之前要分配多少内存.如果文件是结构化的,使得第一个值是记录的数量,您可以执行类似的操作:

size_t recs = 0;
double *temps = NULL;

FILE *fp = fopen ( filename, "r" );
if ( fp )
{
  if ( fscanf( fp, "%zu", &recs ) == 1 )
  {
    temps = malloc( sizeof *temps * recs );
    if ( temps )
    {
      // read contents of file into temps
    }
  }
}

有时您需要分配非常大对象,类似于

int ginormous[1000][1000][1000];

假设4字节整数,此数组需要4GB.不幸的是,堆栈帧(其中局部变量保持在大多数架构上)往往比这要小得多,因此尝试分配太多的内存可能导致运行时错误(并且通常是).动态内存池(A.K.A.HEAP)通常比堆栈大的要大得多,更少任何一个堆栈帧.所以对于令人讨厌的东西,你需要写一些像

这样的东西
int (*ginormous)[1000][1000] = malloc( sizeof *ginormous * 1000 );

它仍然可以为像这样的请求失败;如果堆足够分段,则可能没有足够大的单个连续块来处理请求.如有必要,您可以做零碎的分配;行不一定在内存中邻近,但它更有可能抓住所需的所有内存:

int ***ginormous = malloc( sizeof *ginormous * 1000 );
if ( ginormous )
{
  for ( size_t i = 0; i < 1000; i++ )
  {
    ginormous[i] = malloc( sizeof *ginormous[i] * 1000 );
    if ( ginormous[i] )
    {
      ginormous[i][j] = malloc ( sizeof *ginormous[i][j] * 1000 );
      if ( ginormous[i][j] )
      {
        // initialize ginormous[i][j][k]
      }
    }
  }
}

最后,动态内存允许您构建可以在添加或删除数据(如列表,树,队列等)时增长和缩小的容器.您甚至可以构建可以成长的自己的真实"字符串"数据类型当您将字符附加到它(类似于C ++中的string类型)时.

其他推荐答案

当您不知道内存的最坏情况要求时,需要动态分配.然后,静态分配必要的内存是不可能的,因为你不知道你需要多少.

即使您知道最坏的情况要求,也可能需要使用动态内存分配.它允许系统存储器通过多个进程更有效地使用.所有进程都可以静态提交其最坏的情况内存要求,但是系统上可以将帽子放在系统上可以存在多少运行进程.如果所有进程都不是同时使用最坏情况的情况,那么系统内存不断运行未充分利用,这是浪费资源.

至于您的侧面问题,您不应将呼叫的结果转换为c中的呼叫结果.它可以隐藏丢失声明的错误(在C.99之前允许隐式声明),并导致未定义的行为.始终更喜欢在没有演员的情况下取出malloc()的结果.声明malloc()返回void *,在c中,始终允许在c4>和另一个指针类型之间的转换(Modulo类型限定符如const).

其他推荐答案

作为侧面笔记,什么是:ptr_one = (int *)malloc(sizeof(int))和int *ptr_one = malloc(sizeof(int))

这个.

首先,我知道这可能是一个荒谬的问题,因为动态内存分配是C编程中非常重要的话题.但是,我一直无法找到它使我们能做的事情的良好解释,或者为什么需要它.

与堆栈相比,内存池(或更常见的堆)非常大.考虑这两个例子,为什么在堆栈上使用内存池是有用的:

1.如果您定义了一个数组并且希望它持续到多个堆栈框架中,该怎么办?当然,您可以将其声明为全局变量,它将存储在内存的全局数据部分中,但这会因为您的程序变大而较大而变得混乱.或者,您可以将其存储在内存池上.

int *func( int k ) {
  assert( k >= 1 );

  int *ptr_block = malloc( sizeof( int ) * k );

  if ( ptr_block == NULL ) exit( EXIT_FAILURE );

  for ( int i = 0; i < k; i++ ) {
    ptr_block[ i ] = i + 1;
  }

  return ptr_block; // Valid.
}

......但是如果您在堆栈上定义了阵列,则为 not 工作.原因是,一旦弹出堆栈帧,所有内存地址都可以由另一个堆栈帧(并且因此覆盖)使用,而使用来自内存池的内存将持续到用户(您或客户端)的free D.

2.如果您想实现动态阵列以处理读取任意大量数字序列?您将无法在堆栈上定义您的数组,您需要使用内存池.回想一下,它非常常见(并且强烈推荐,除非您显式需要复制结构)将指针传递给结构,而不是结构本身(因为它们可以相当大).考虑这种动态阵列的小型实现:

struct dyn_array {
  int *arr;
  int len;
  int cap;
};

typedef struct dyn_array *DynArray;

void insert_item( int const item, DynArray dyn_arr ) {
  // Checks pre conditions.
  assert( dyn_arr != NULL );

  // Checks if the capacity is equal to the length. If so, double.
  if ( dyn_arr->cap == dyn_arr->len ) {
    dyn_arr->cap *= 2;

    DynArray new_dyn_arr = malloc( sizeof( int ) * dyn_arr->cap ); // [oo]

    // ... copy, switch pointers and free...
  }

  // ... insert, increase length, etc.
}

...在线[oo]注意,如果在堆栈上定义这一点,则弹出此堆栈帧后,将不再分配阵列的所有内存地址.意思是,另一个堆栈帧(可能是下一个)将使用那些内存地址(或它的某些子集).

备注:从我的代码片段,ptr_block存储在堆栈上:因此&ptr_block是堆栈地址,但是ptr_block的值是来自内存池的某个位置.

本文地址:https://www.itbaoku.cn/post/358977.html