【问题标题】:Realloc only works twiceRealloc 只工作两次
【发布时间】:2019-10-09 04:31:41
【问题描述】:

由于某种原因,当我重新分配一个数组以将一个项目附加到数组时,它在段错误之前只工作了两次。当我尝试打印数组内的字符串时会发生段错误。我目前有一个 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 并不打算用于数组。它用于动态内存分配,同时尝试保留相同的基地址。
  • 这是一个“malloc”数组指针@Mihir
  • 请创建一个minimal reproducible example 重现您遇到的问题。
  • 您在本地对 arr 进行了更改,这些更改不会反映在调用函数中。
  • @dbush 我传入了一个指向 arr 而不是 arr 的指针。因此,当我重新分配指针时,更改会得到反映。

标签: c realloc


【解决方案1】:
  1. 在主函数中,您需要将hist 的第一个元素设置为NULL,因为您在函数apphist 中检查它

    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. 或者,您可以返回一个指向 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);

【讨论】:

  • 这个char** hist = malloc(sizeof(char**)); 必须是char** hist = malloc(sizeof (char*)); 或者更好的char** hist = malloc(sizeof *hist);
  • 还有那些分配给arr[length] = line; arr[length+1] = NULL;的指代内存无效,因为重定位已经发生了。
  • “函数apphist只在本地改变arr的值。” main 的行为证明这是错误的。
  • @thebusybee char ** arr 是函数中的局部变量。更改值arr 本身不会反映在主函数中
  • 4 中的代码仍然被破坏,原因与我在第二条评论中提到的相同(其中“relocation 应为“reallocation”)。
【解决方案2】:

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

【讨论】:

  • 它是一个指针数组...它是 1 有意义吗?我做错了吗?
【解决方案3】:

对于一般情况,我认为您只需要使用长度 +1 调用 realloc

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

这是因为您已经为先前状态的 NULL 终止指针提供了空间。使用您提出的代码,正在发生的事情是这样的

//重新分配之前的状态

字符串字符串NULL

// apparr() 调用

String String NULL undefined undefined // realloc

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

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

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

【讨论】:

  • strlen("string") + 1 部分是正确的。我不确定我是否遵循其余的,但关键是 strcpy() 操作写入分配数组的边界之外。
【解决方案4】:

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

下面找到 append-element-to-array 函数的更通用实现:

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

【讨论】:

    【解决方案5】:

    您的代码中有几个错误:

    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()
    • 检查任何分配的返回值是否为 NULL,这意味着分配失败。
    • 你不用argcargv,所以写int main(void)
    • 第一个分配的类型“错误”,但都是指针,所以大小相同:char** hist = malloc(sizeof(char*));
    • 无需转换malloc() 返回的指针,因为它返回指向void 的指针。指向void 的指针和其他指针可以在没有强制转换的情况下来回分配。
    • 您可以将 malloc()/strcpy() 对替换为 strdup()
    • 您甚至可以调用apphist(),将字符串作为“立即”值,如下所示:apphist(hist, "test");
    • main() 应该返回一个 intEXIT_SUCCESS 是正确的值。
    • 您可以在参数和声明中添加一些const 以使事情更安全。但是想想什么是不变的。

    【讨论】:

    • 这是取自一段更大的代码。我添加了 mcve,因为人们很不高兴,想不出如何自己使用原始代码。我在更大的代码价格中使用 argc 和 argv。为了清楚起见,我投了。请考虑一下我在其中使用的程序如何比显示给您的程序大。我向您展示的内容是为了清晰而不是完整。
    • 这里显示的大多数来源只是摘录。没有冒犯,但需要帮助的是您,而不是我们。 ;-) 所以,请花时间准备一个清晰的示例,而不是让每个人都必须找出缺少的内容并添加它。如果一位代码专家(即您)每次比其他多位专家多投入一点时间,效率会高得多。
    • 关于“为清楚起见”:需要澄清什么? char * 类型的变量接收一个指向内存的指针,该内存分配为某个数量的char。无需额外演员。
    猜你喜欢
    • 1970-01-01
    • 2021-08-31
    • 1970-01-01
    • 2011-09-16
    • 1970-01-01
    • 2015-07-12
    • 2017-01-06
    • 2018-06-22
    • 2015-09-18
    相关资源
    最近更新 更多