Realloc 只工作两次

Realloc only works twice

出于某种原因,当我重新分配一个数组以将一个项目附加到数组时,它在段错误之前只工作了两次。当我尝试打印数组内的字符串时发生段错误。我目前有一个 NULL 终止的数组。

void apparr(char** arr, char* line) {
    int length = 0;
    // find the length of the array
    while(arr[length] != NULL) {
        length++;
    }
    // realloc with 2 extra spaces (1 for line, 1 for NULL)
    arr = realloc(arr, sizeof(char*) * (length+2));
    // set last element (which was NULL) to line
    arr[length] = line;
    // set the NULL terminator
    arr[length+1] = NULL;
}

我不知道我哪里会出错,我唯一的猜测是我如何调用 realloc。但是,我会理解不适用于 1 次调整大小,但我不知道为什么这适用于两次调整大小然后在我打印回数组时出现段错误。

如何在 main 中使用:

int main(int argc, char** argv){
char** hist = malloc(sizeof(char**));
char* linep1;
char* linep2;
char* linep3;
char* linep4;
linep1 = (char*)malloc(strlen("test")*sizeof(char));
linep2 = (char*)malloc(strlen("test2")*sizeof(char));
linep3 = (char*)malloc(strlen("test3")*sizeof(char));
linep4 = (char*)malloc(strlen("test4")*sizeof(char));   
strcpy(linep1, "test");
strcpy(linep2, "test2");
strcpy(linep3, "test3");
strcpy(linep4, "test4");
apphist(hist, linep1);
apphist(hist, linep2);
//apphist(hist, linep3); //uncommenting this line causes nothing to be printed
//apphist(hist, linep4); //uncommenting this line causes only test4 to be printed
int x = 0;
while (hist[x] != NULL) {
    printf("%s\n", hist[x]);
    x++;
}
}

我认为你应该在 realloc 中使用之前取消对 arr 的引用。另一个观察; sizeof(char*) 在 32 位架构中通常为 4,在 64 位架构中通常为 8 而不是 1。

对于一般情况我认为你只需要调用长度为+1的realloc

arr = realloc(arr, sizeof(char*) * (length+1));

这是因为您已经 space 用于先前状态的 NULL 终止指针。使用您提出的代码,发生的事情是这样的

// 重新分配之前的状态

字符串字符串 NULL

// apparr() 调用

String String NULL undefined undefined // 重新分配

String String String undefined undefined // arr[length] = line;

String String String NULL undefined // arr[length+1] = NULL;

第一次它会工作(在最后留下悬空分配的节点)但第二次它可能会由于额外分配而以多种方式崩溃。

  1. 在主函数中,您需要将 hist 的第一个元素设置为 NULL,因为您在函数 apphist[=21= 中检查它]

    char** hist = malloc(sizeof(char*));
    *hist = NULL;
    
  2. 函数apphist只在局部改变arr的值。为了反映主函数的变化,您需要传递一个指向 arr 的指针,即一个 3D 指针 char ***arr

  3. 您应该始终检查 realloc 的结果并在失败时执行操作。

函数代码如下。

void apparr(char*** arr2, char* line) {
    int length = 0;
    char **arr = *arr2;
    while(arr[length] != NULL) {
        length++;
    }
    arr = realloc(arr, sizeof(char*) * (length+2));
    if (arr == NULL) {
        exit(1); // handle error 
    }        
    *arr2 = arr;   
    arr[length] = line;
    arr[length+1] = NULL;
}
  1. 或者,您可以 return 一个指向 char 的指针并更新 main 中的值。
char** apparr(char** arr, char* line) {
    int length = 0;
    char **temp;
    while(arr[length] != NULL) {
        length++;
    }
    temp = realloc(arr, sizeof(char*) * (length+2));
    if (temp == NULL) {
        exit(1); // handle error 
    }
    arr = temp;
    arr[length] = line;
    arr[length+1] = NULL;
    return (arr);
}    

//in main
hist = apphist(hist, linep1);
hist = apphist(hist, linep2);

您的代码中有几个错误:

  1. 这是你的主要错误。你没有替换给定指针中的值。使用指向指针的指针是正确的,但你需要取消引用它。为此,您需要将指针传递给 hist 并在重新分配时取消引用它:

    *arr = realloc(*arr, sizeof(char*) * (length + 2));
    
  2. 指针列表未初始化,第一次分配后需要设置第一个指针:

    hist[0] = NULL;
    
  3. 您的测试字符串分配为 1:

    linep1 = malloc((strlen("test") + 1) * sizeof(char));
    linep2 = malloc((strlen("test2") + 1) * sizeof(char));
    linep3 = malloc((strlen("test3") + 1) * sizeof(char));
    linep4 = malloc((strlen("test4") + 1) * sizeof(char));
    

补充说明:

  • 缺少完整的包含 minimal reproducable example
  • 名称apparr()错误,您在main()中调用apphist()
  • 检查任何分配的 return 值是否为 NULL,这意味着分配失败。
  • 你没有使用argcargv,所以写int main(void)
  • 第一个分配有 "wrong" 类型,但都是指针,所以大小相同:char** hist = malloc(sizeof(char*));
  • 不需要转换由 malloc() 编辑的 return 指针,因为它 return 是指向 void 的指针。指向 void 的指针和其他指针可以在没有转换的情况下来回分配。
  • 您可以将 malloc()/strcpy() 对替换为 strdup()
  • 您甚至可以使用字符串作为 "immediate" 值来调用 apphist(),如下所示:apphist(hist, "test");
  • main() 应该 return 一个 intEXIT_SUCCESS 是正确的值。
  • 您可以在参数和声明处放置一些 const 以使事情更安全。但是想想什么是不变的

其他人已经提到了所有错误和可能的陷阱。

下面是追加元素到数组函数的更通用的实现:

#include <stdlib.h>
#include <errno.h> /* for EINVAL */

int apparr(char *** parr, char * line) {
  size_t length = 0;

  if (NULL == *parr) {
    if (NULL != line) {
      errno = EINVAL;
      return -1;
    }
  } else {
    // find the length of the array
    while (NULL != (*parr)[length]) {
      ++length;
    }
  }

  {
    // realloc with 2 extra spaces (1 for line, 1 for NULL)
    void * pv = realloc(*parr, (length+1) * sizeof **parr);
    if (NULL == pv) {
      return -1; /* By convention -1 indicates failure. */
    }

    *parr = pv;
  }

  (*parr)[length] = line;
  if (0 < length) {
    (*parr)[length + 1] = NULL;
  }

  return 0; /* By convention 0 indicates success. */
}

并像这样使用它:

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

int apparr(char *** parr, char * line) {

int main(int argc, char** argv) {
  char ** hist = NULL;
  char * linep1;
  char * linep2;
  char * linep3;
  char * linep4;

  if (-1 == apparr(&hist, NULL)) {
    perror("apphist() failed initially\n");
    exit(EXIT_FAILURE);
  }

  linep1 = malloc(strlen("test") + 1);
  linep2 = malloc(strlen("test2") + 1); /* +1 for the c-string's 0-termination; sizeof (char) is 1 by definition */
  linep3 = malloc(strlen("test3") + 1);
  linep4 = malloc(strlen("test4") + 1);

  strcpy(linep1, "test");
  strcpy(linep2, "test2");
  strcpy(linep3, "test3");
  strcpy(linep4, "test4");

  if (-1 == apphist(&hist, linep1)) {
    perror("apphist() failed for line 1\n");
    exit(EXIT_FAILURE);
  }

  if (-1 == apphist(&hist, linep2) {
    perror("apphist() failed for line 2\n");
    exit(EXIT_FAILURE);
  }

  if (-1 == apphist(&hist, linep3) {
    perror("apphist() failed for line 3\n");
    exit(EXIT_FAILURE);
  }

  if (-1 == apphist(&hist, linep4)  {
    perror("apphist() failed for line 4\n");
    exit(EXIT_FAILURE);
  }

  {
    size_t x = 0;
    while (hist[x] != NULL) {
      printf("%s\n", hist[x]);
      ++x;
    }
  }
}