c中结构中的动态分配内存

Dynamically allocated memory in structure in c

我正在尝试创建两个列表,pros 和 cons,然后打印它们。 但是我不知道我做错了什么。

我试着用gdb在线调试程序,发现错误在函数fgets()。

#include <stdio.h>
#include <string.h>
typedef struct list{
    char ** reason;

} list;
void printMenu();
void printList(list * myList, int len1);


int main(void)
{
    int keepGoing = 0;
    int choice = 0;
    int i = 0;
    int j = 0;
    list * pros;
    list * cons;

    while (!keepGoing){

        printMenu(); 
        scanf("%d", &choice);

        pros = (list*)malloc(sizeof(list));
        cons = (list*)malloc(sizeof(list));
        switch (choice){
        case 1:
            i++;
            printf("Enter a reason to add to list PRO: ");
            pros = (list*)realloc(pros, i*sizeof(list));
            fgets(pros->reason[i], 50, stdin);
            pros->reason[strcspn(pros->reason[i], "\n")] = 0;
            break;
        case 2:
            j++;
            cons = (list*)realloc(cons->reason, j*sizeof(list));
            printf("Enter a reason to add to list CON: ");
            fgets(cons->reason[j], 50, stdin);
            cons->reason[strcspn(cons->reason[j], "\n")] = 0;
            break;
        case 3:
            printf("PROS:\n");
            printList(pros, i);
            printf("CONS:\n");
            printList(cons, j);

            break;
        case 4:
            keepGoing = 1;
            break;
        default:
            printf("Invalid value.");
            keepGoing = 1;
        }
    }

    free(pros);
    free(cons);

    getchar();
    return 0;
}

void printList(list * reasons, int len1){
    int i = 0;
    for (i = 0; i < len1; i++){
        printf("%s\n", reasons->reason[i]);
    }
}
void printMenu(){
    printf("Choose option:\n");
    printf("1 - Add PRO reason\n");
    printf("2 - Add CON reason\n");
    printf("3 - Print reasons\n");
    printf("4 - Exit\n");
}

你有问题

  fgets(pros->reason[i], 50, stdin);

因为您想要使用 的内存无效pros->reason 没有指向有效的内存,所以你不能取消引用它,这会导致 undefined behavior.

在索引到 pros->reason 之前,您需要使 pros->reason 指向一个有效的内存位置。

之后,如果您希望 pros->reason[i]s 用作 fgets()destination,则还需要指向有效内存.


除此问题外,您还有另一个问题使此代码变得毫无意义,即在循环的每次迭代中调用 malloc()。您只需要调用 malloc() 一次,即可获得由内存分配器函数分配的(指向内存的)指针,然后在循环内使用 realloc()adjust到所需的内存。

无需动态分配这些:list * pros; list * cons;pros = (list*)realloc(pros, i*sizeof(list)); 这样的代码没有任何意义。

相反,将它们声明为普通变量。 list pros

您需要动态分配的是成员 pros.reason。您需要分配一个它指向的指针数组,然后您需要分配各个数组。

有很多问题。以前的评论和答案仍然适用。

这是一个干净的解决方案。

  • 列表结构现在是自包含的,无需跟踪单独变量中的字符串数
  • 添加了独立 AddString 功能
  • 不再是不必要的mallocs
  • 所有分配的内存都被正确释放
  • 删除了一些逻辑错误(keepGoing 的反逻辑)

还有改进的余地。特别是内存分配函数没有错误检查。


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

typedef struct list {
  int size;       // number of strings
  int chunksize;  // current of chunk
  char ** reason;
} list;

void printMenu();
void printList(list * reasons);
void freeList(list * l);
void AddString(list *l, const char *string);

int main(void)
{
  int keepGoing = 1;
  int choice = 0;

  list pros = { 0 };  // = {0} initializes all fields to 0
  list cons = { 0 };

  while (keepGoing) {

    printMenu();
    scanf("%d", &choice);

    char input[50];
    fgets(input, sizeof(input), stdin);  // absorb \n from scanf

    switch (choice) {
    case 1:
      printf("Enter a reason to add to list PRO: ");
      fgets(input, sizeof(input), stdin);
      AddString(&pros, input);    // Add string to pros
      break;
    case 2:
      printf("Enter a reason to add to list CONS: ");
      fgets(input, sizeof(input), stdin);
      AddString(&cons, input);    // Add string to cons
      break;
    case 3:
      printf("PROS:\n");
      printList(&pros);
      printf("CONS:\n");
      printList(&cons);
      break;
    case 4:
      keepGoing = 0;
      break;
    default:
      printf("Invalid value.");
      keepGoing = 1;
    }
  }

  freeList(&pros);
  freeList(&cons);

  getchar();
  return 0;
}


#define CHUNKSIZE 10

void AddString(list *l, const char *string)
{
  if (l->size == l->chunksize)
  {
    // resize the reason pointer every CHUNKSIZE entries
    l->chunksize = (l->chunksize + CHUNKSIZE);

    // Initially l->reason is NULL and it's OK to realloc a NULL pointer
    l->reason = realloc(l->reason, sizeof(char**) * l->chunksize);
  }

  // allocate memory for string (+1 for NUL terminator)
  l->reason[l->size] = malloc(strlen(string) + 1);

  // copy the string to newly allocated memory
  strcpy(l->reason[l->size], string);

  // increase number of strings
  l->size++;
}

void freeList(list * l) {
  for (int i = 0; i < l->size; i++) {
    // free string
    free(l->reason[i]);
  }

  // free the list of pointers
  free(l->reason);
}

void printList(list * l) {
  for (int i = 0; i < l->size; i++) {
    printf("%s\n", l->reason[i]);
  }
}

void printMenu() {
  printf("Choose option:\n");
  printf("1 - Add PRO reason\n");
  printf("2 - Add CON reason\n");
  printf("3 - Print reasons\n");
  printf("4 - Exit\n");
}