C语言 为什么或何时需要在 C 中动态分配内存?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18217525/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 07:09:38  来源:igfitidea点击:

Why, or when, do you need to dynamically allocate memory in C?

cmallocdynamic-memory-allocation

提问by user2517777

Dynamic memory allocation is a very important topic in C programming. However, I've been unable to find a good explanation of what this enables us to do, or why it is required.

动态内存分配是 C 编程中一个非常重要的话题。但是,我一直无法很好地解释这使我们能够做什么,或者为什么需要它。

Can't we just declare variables and structs and never have to use malloc()?

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

As a side note, what is the difference between:

作为旁注,有什么区别:

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

and

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

回答by John Bode

You need to use dynamic memory when:

在以下情况下需要使用动态内存:

  • You cannot determine the maximum amount of memory to use at compile time;
  • You want to allocate a verylarge object;
  • You want to build data structures (containers) without a fixed upper size;
  • 您无法确定编译时要使用的最大内存量;
  • 你想分配一个非常大的对象;
  • 您想构建没有固定上限的数据结构(容器);

You don't always know how much memory you will need to set aside at compile time. Imagine processing a data file (a time series of temperatures, say), where the number of records in the file isn't fixed. You could have as few as 10 records or as many as 100000. If you want to read all that data into memory to process it, you won't know how much memory to allocate until you read the file. If the file is structured so that the very first value is the number of records, you could do something like this:

您并不总是知道在编译时需要留出多少内存。想象一下处理一个数据文件(比如温度的时间序列),其中文件中的记录数是不固定的。您可以拥有少至 10 条记录或多至 100000 条记录。如果您想将所有数据读入内存以进行处理,那么在读取文件之前您将不知道要分配多少内存。如果文件的结构使得第一个值是记录数,您可以执行以下操作:

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
    }
  }
}

Sometimes you need to allocate a verylarge object, something like

有时你需要分配一个非常大的对象,比如

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

Assuming a 4-byte integer, this array will require 4GB. Unfortunately, stack frames (where local variables are kept on most architectures) tend to be much smaller than that, so trying to allocate that much memory may lead to a run-time error (and typically does). The dynamic memory pool (a.k.a. the heap) is typically muchlarger than the stack, much less any one stack frame. so for something that obnoxious you'd need to write something like

假设一个 4 字节的整数,这个数组将需要 4GB。不幸的是,堆栈帧(在大多数架构上保留局部变量)往往比这小得多,因此尝试分配那么多内存可能会导致运行时错误(通常会)。动态存储器池(又名堆)通常小于堆越大,要少得多任一项堆栈帧。所以对于那些令人讨厌的东西,你需要写一些类似的东西

int (*ginormous)[1000][1000] = malloc( sizeof *ginormous * 1000 );

It's still possible for a request like that to fail; if your heap is fragemented enough, you may not have a single contiguous block large enough to hande the request. If necessary, you could do a piecemeal allocation; rows won't necessarily be adjacent in memory, but it's more likely you'll be able to grab all the memory you need:

这样的请求仍然有可能失败;如果您的堆碎片足够多,您可能没有足够大的单个连续块来处理请求。如有必要,您可以进行零碎分配;行在内存中不一定相邻,但更有可能获得所需的所有内存:

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]
      }
    }
  }
}

And finally, dynamic memory allows you to build containers that can grow and shrink as you add or remove data, such as lists, trees, queues, etc. You could even build your own real "string" data type that can grow as you append characters to it (similar to the stringtype in C++).

最后,动态内存允许您构建可以随着您添加或删除数据而增长和缩小的容器,例如列表、树、队列等。您甚至可以构建您自己的真正“字符串”数据类型,它可以随着您的追加而增长字符(类似于stringC++ 中的类型)。

回答by jxh

Dynamic allocation is required when you don't know the worst case requirements for memory. Then, it is impossible to statically allocate the necessary memory, because you don't know how much you will need.

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

Even if you know the worst case requirements, it may still be desirable to use dynamic memory allocation. It allows for the system memory to be used more efficiently by multiple processes. All processes could statically commit their worst case memory requirements, but that puts a cap on how many running processes can exist on the system. If it is never the case that all processes use the worst case at the same time, then the system memory constantly runs underutilized, which is a waste of resources.

即使您知道最坏情况的要求,使用动态内存分配可能仍然是可取的。它允许多个进程更有效地使用系统内存。所有进程都可以静态提交其最坏情况下的内存需求,但这限制了系统上可以存在的运行进程的数量。如果从来没有所有进程同时使用最坏情况,那么系统内存不断地运行未充分利用,这是一种资源浪费。

As to your side question, you should not cast the result of a call to malloc()in C. It can hide the bug of a missing declaration (implicit declarations were permitted prior to C.99), and results in undefined behavior. Always prefer taking the result of malloc()without a cast. malloc()is declared to return void *, and in C, a conversion between void *and another pointer type is always permitted (modulo type qualifiers like const).

至于你的附带问题,你不应该malloc()在 C 中转换调用的结果。它可以隐藏缺少声明的错误(在 C.99 之前允许隐式声明),并导致未定义的行为。总是喜欢采用malloc()没有演员表的结果。malloc()声明为 return void *,并且在 C 中,void *始终允许在和另一个指针类型之间进行转换(模类型限定符,如const)。

回答by Jacob Pollack

As a side note, what is the difference between: ptr_one = (int *)malloc(sizeof(int))and int *ptr_one = malloc(sizeof(int))

作为旁注,有什么区别:ptr_one = (int *)malloc(sizeof(int))int *ptr_one = malloc(sizeof(int))

See this.

看到这个

Firstly, I know this is likely a ridiculous question, as dynamic memory allocation is a very important topic in C programming. However, I've been unable to find a good explanation of what this enables us to do, or why it is required.

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

The memory pool (or more commonly the heap) is very large in comparison to the stack. Consider these two examples for why it's useful to use the memory pool over the stack:

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

1.What if you defined an array and wanted it to persist amongst multiple stack frames? Sure, you could declare it as a global variable and it will be stored in the global data section of memory, however this will get cluttered as your program gets larger and larger. Alternatively, you could store it on the memory pool.

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.
}

... this would however notwork if you defined your array on the stack. Reason being, once a stack frame is popped all the memory addresses can be used by another stack frame (and hence overwritten), whereas using memory from the memory pool will persist until freed by the user (you, or client).

......但这会不会,如果你在堆栈上定义了阵列工作。原因是,一旦弹出堆栈帧,所有内存地址都可以被另一个堆栈帧使用(并因此被覆盖),而使用内存池中的内存将持续到free用户(您或客户端)。

2.What if you wanted to implement a dynamic array to handle reading an arbitrary large sequence of numbers? You would not be able to do this defining your array on the stack, you would need to use the memory pool. Recall that it's extremely common (and highly recommended unless you explicitly need to copy a struct) to be passing a pointer to a struct, never the struct itself (as they can be rather large). Consider this small implementation of a dynamic array:

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.
}

... on line [oo]notice that if this were defined on the stack then once this stack frame is popped all the memory addresses for the array would no longer be allocated. Meaning, another stack frame (likely the next one) will be using those memory addresses (or some subset of it).

...在线[oo]注意,如果这是在堆栈上定义的,那么一旦弹出这个堆栈帧,数组的所有内存地址将不再分配。这意味着,另一个堆栈帧(可能是下一个)将使用这些内存地址(或其中的某个子集)。

Remark:From my snippet of code, ptr_blockis stored on the stack: hence &ptr_blockis a stack address, however the value of ptr_blockis somewhere from the memory pool.

备注:从我的代码片段中,ptr_block存储在堆栈中:因此&ptr_block是堆栈地址,但是 的值ptr_block来自内存池的某处。