簡體   English   中英

如何在C中動態分配內存並確定數組大小?

[英]How do I allocate memory and determine array sizes dynamically in C?

我試圖從python背景教自己C. 我目前的小問題是嘗試對數組長度等內容進行較少的硬編碼,並根據輸入動態分配內存。

我寫了以下程序。 我希望社區的建議可以通過以下方式進行修改:

1.)使firstlast Name元素變長。 目前,他們的長度被硬編碼為MAX_NAME_LENGTH 這將涉及更改Namestruct聲明以及我為其元素賦值的方式。

2.)Bonus:找出一些方法來逐步向name_list數組中添加新元素,而無需事先確定其長度。 基本上使它成為可擴展的列表。

/* namelist.c 

   Loads up a list of names from a file to then do something with them.

*/
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>

#define DATAFILE "name_list.txt"
#define DATAFILE_FORMAT "%[^,]%*c%[^\n]%*c"
#define MAX_NAME_LENGTH 100

typedef struct {
  char first[MAX_NAME_LENGTH];
  char last[MAX_NAME_LENGTH];
} Name;


int main() {
  FILE *fp = fopen(DATAFILE, "r");

  // Get the number of names in DATAFILE at runtime.
  Name aName;
  int lc = 0;
  while ((fscanf(fp, DATAFILE_FORMAT, aName.last, aName.first))!=EOF) lc++;
  Name *name_list[lc];

  // Now actually pull the data out of the file
  rewind(fp);
  int n = 0;
  while ((fscanf(fp, DATAFILE_FORMAT, aName.last, aName.first))!=EOF)
  {
    Name *newName = malloc(sizeof(Name));
    if (newName == NULL) {
      puts("Warning: Was not able to allocate memory for ``Name`` ``newName``on the heap.");
    } 
    memcpy(newName, &aName, sizeof(Name));
  name_list[n] = newName;
  n++;
  }

  int i = 1;
  for (--n; n >= 0; n--, i++) {
    printf("%d: %s %s\n", i, name_list[n]->first, name_list[n]->last);
    free(name_list[n]);
    name_list[n] = NULL;
  }

  fclose(fp);
  return 0;
}

name_list.txt示例內容:

Washington,George
Adams,John 
Jefferson,Thomas
Madison,James

更新1:

我已經實現了鏈接列表和@Williham建議的一些輔助函數,結果如下。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define DATAFILE "name_list.txt"
#define MAX_NAME_LENGTH 30
#define DATAFILE_FORMAT "%29[^,]%*c%29[^\n]%*c"

static const int INPUT_BUFFER_SIZE_DEFAULT = sizeof(char) * MAX_NAME_LENGTH;

typedef struct _Name Name;

struct _Name {
  char *first;
  char *last;
  Name *next;
};

int get_charcount(char *str);

Name * create_name_list(char *filename);
void print_name_list(Name *name);
void free_name_list (Name *name);

int main() {

  // Read a list of names into memory and 
  // return the head of the linked list.
  Name *head = create_name_list(DATAFILE);

  // Now do something with all this data.
  print_name_list(head);

  // If you love something, let it go.
  free_name_list(head);
  head = NULL;
  return 0;
}

int get_charcount (char *str) 
{
  int input_length = 1;
    while (str[input_length] != '\0')
    {
      input_length++;
    }
  return input_length;
}

Name * create_name_list(char *filename)
{
  FILE *fp = fopen(DATAFILE, "r");
  char *first_input_buffer = malloc(INPUT_BUFFER_SIZE_DEFAULT);
  char *last_input_buffer = malloc(INPUT_BUFFER_SIZE_DEFAULT);

  Name *firstNamePtr;
  Name *previousNamePtr;
  while ((fscanf(fp, DATAFILE_FORMAT, last_input_buffer, first_input_buffer))!=EOF)
  {
    Name *newNamePtr = malloc(sizeof(Name));

    if (previousNamePtr) 
    {
      previousNamePtr->next = newNamePtr;
      previousNamePtr = newNamePtr;
    } 
    else 
    {
      firstNamePtr = previousNamePtr = newNamePtr;
    }

    char *temp_buffer = malloc(get_charcount(first_input_buffer));
    strcpy(temp_buffer, first_input_buffer);
    newNamePtr->first = malloc(get_charcount(first_input_buffer));
    strcpy(newNamePtr->first, temp_buffer);


    realloc(temp_buffer, get_charcount(last_input_buffer));

    strcpy(temp_buffer, last_input_buffer);
    newNamePtr->last = malloc(get_charcount(last_input_buffer));
    strcpy(newNamePtr->last, temp_buffer);

    free(temp_buffer);
    temp_buffer = NULL;
  }
  previousNamePtr->next = NULL;
  previousNamePtr = NULL;
  free(first_input_buffer);
  free(last_input_buffer);
  first_input_buffer = NULL;
  last_input_buffer = NULL;
  fclose(fp);

  return firstNamePtr;
}

void print_name_list (Name *name)
{
  static int first_iteration = 1;
  if (first_iteration) 
  {
    printf("\nList of Names\n");
    printf("=============\n");
    first_iteration--;
  }
  printf("%s %s\n",name->first, name->last);
  if (name->next)
    print_name_list(name->next);
  else
    printf("\n");
}

void free_name_list (Name *name)
{
  if (name->next)
    free_name_list(name->next);
  free(name->first);
  free(name->last);
  name->first = NULL;
  name->last = NULL;
  name->next = NULL;
  free(name);
  name = NULL;
}

一個非常簡單的方法是根本不使用數組,而是使用鏈表:

這可以通過多種方式完成,但最簡單的可能是修改Name結構,如下所示:

typedef struct _Name Name;

struct _Name {
  char *first;
  char *last;
  Name *next;
};

使用char *而不是char []將需要一些strcpy ,但這真的既不在這里也不在那里。 要擴展數組,您現在可以一次一個地對這些元素進行malloc; 然后適當地設置。

注意:在創建新的尾部元素時,請記住在NULL旁邊設置。

沒有辦法在C中擴展數組。您可以做的就是分配更大的內存塊並復制項目。 這就是Python所做的事情。

還沒有辦法確定數組的大小,因為它只存儲項目沒有附加信息(Python數組存儲元素旁邊的長度。)你可以在數組的末尾放置一個標記並計算元素,直到你擊中標記,這是字符串的工作方式(null char'\\ 0'是標記。)

你必須有一個最大長度數組來接受輸入 - 你不知道輸入將持續多長時間。 但是,您只需要一個該長度的數組。 獲得輸入后,您可以獲得它的長度並分配一個恰當大小的數組來存儲名稱。

至於增加列表,您可以使用realloc或使用鏈接列表。

你開始閱讀鏈接列表嗎? 它可以幫助您擁有動態結構。

您還可以檢查數組和鏈表之間的區別。 char指針可以是鏈表的成員,可以動態分配內存。

從stackoverflow檢查此鏈接以獲取相同的更多信息。

. 由於還沒有人提到它,如果使用* scanf讀取不受信任的輸入字符串,則應始終使用 喜歡

scanf("%19s", str);

請注意,這指定了不包括終止NUL的最大字符串長度,因此在此示例中,str應為20個字符長。

如果不限制像這樣的scanf輸入轉換,則在緩沖區溢出時,您不僅會獲得未定義的行為,還會出現安全漏洞。

回到你關於恐龍增長緩沖的問題。 想象一下,您正在讀取由線組成的輸入,每條線最多可以有78個字符寬。 在這種情況下,您知道每行的最大長度,但不知道輸入的最大長度。 執行此操作的常用方法是分配一些默認空間,如果需要增大該空間,請將其增長* 2,這樣您就不需要多次重新分配()。

暫無
暫無

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

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