簡體   English   中英

為什么或何時需要在C中動態分配內存?

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

動態內存分配是C編程中非常重要的主題。 但是,我無法找到一個很好的解釋,這使我們能夠做到這一點,或者為什么需要這樣做。

我們不能只聲明變量和結構,而不必使用malloc()嗎?

作為旁注,有什么區別:

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

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

在以下情況下需要使用動態內存:

  • 您無法確定在編譯時使用的最大內存量;
  • 你想分配一個非常大的對象;
  • 您希望構建沒有固定大小的數據結構(容器);

您並不總是知道在編譯時需要留出多少內存。 想象一下處理一個數據文件(比如溫度的時間序列),其中文件中的記錄數量不固定。 您可以只有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
    }
  }
}

有時您需要分配一個非常大的對象,例如

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

假設一個4字節的整數,這個數組將需要4GB。 不幸的是,堆棧幀(其中局部變量保留在大多數架構上)往往比這小得多,因此嘗試分配那么多內存可能會導致運行時錯誤(通常會發生)。 動態存儲器池(又名堆)通常比堆大得多 ,少得多的任一項堆棧幀。 所以對於令人討厭的東西,你需要寫一些類似的東西

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中調用malloc()的調用結果。它可以隱藏缺失聲明的錯誤(在C.99之前允許隱式聲明),並導致未定義的行為。 總是喜歡在沒有malloc()情況下獲取malloc()的結果。 malloc()被聲明為返回void * ,並且在C中,始終允許void *和另一個指針類型之間的轉換(模數類型限定符,如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.
}

...如果您在堆棧上定義了數組,這將無法工作。 原因是,一旦彈出堆棧幀,所有內存地址都可以由另一個堆棧幀使用(因此被覆蓋),而使用內存池中的內存將持續到用戶(您或客戶端) 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的值是從內存池中的某個位置。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM