c语言 - 创建一个动态数组,但将分段错误作为错误



我想创建一个包含用户输入的动态数组。但是在第一次输入后,我一直收到分段错误作为错误。我知道分段错误是由于错误的内存访问引起的。有没有办法在代码中找到错误?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int length,i;
int size_arr = 1;
int size_word = 102;
char **arr;
char *word;
arr = malloc(size_arr * sizeof(char*));
word = malloc(size_word *sizeof(char));
if(arr == NULL)
{
fprintf(stderr, "No reallocation");
exit(EXIT_FAILURE);
}
else
{
while(!feof(stdin) && fgets(word, size_word , stdin) != NULL)
{   
arr[i] = strndup(word,size_word);
length = strlen(word);
if(length > 102)
{
fprintf(stderr, "Word is too long");
}
if(size_arr-1 == i)
{
size_arr *= 2;
arr = realloc(arr, size_arr * sizeof(char*));
}
i++;
}

}
free(arr);
return 0;
}

问候 索雷塞斯

你很接近,你只是对如何处理将所有部分放在一起有点困惑。首先,了解代码中不涉及数组。当您使用指针到指针(例如char **arr),这是一个两步过程。arr本身是指向已分配指针块的单指针 - 每次通过调用realloc()来重新分配额外的指针,将其扩展一个以添加下一个单词。

第二步是为每个单词分配存储空间。然后,将该单词存储的起始地址分配给已分配的指针。

因此,您有一个指针块,您可以扩展(realloc())以添加一个指针来保存下一个单词的地址,然后为该单词分配存储空间,将起始地址分配给新指针,然后将单词复制到该地址。

(注意:调用realloc()每次迭代以仅添加一个指针是低效的。您可以通过添加另一个计数器(例如allocated),用于保存分配的指针数和计数器used,以跟踪您使用的指针数。只有在used == allocated时才能重新分配。这样你就可以,例如每次可用的指针数量增加一倍 - 或者你选择的任何增长方案)

另请注意,strdup()strndup()很方便,但不是标准 C 库的一部分(它们是 POSIX)。虽然大多数编译器都会提供它们,但您可能需要正确的编译器选项来确保它们可用。

让我们看一下您的示例,在简单的情况下,仅使用标准库提供的函数。我们将按一个方案重新分配,并让您实施used == allocated方案以稍后清理。

读取数据行时,在读取该行之前,您将不知道需要存储多少个字符 - 因此只需重复使用相同的固定大小缓冲区来读取每一行。然后,您可以修剪fgets()包含的'n',并获取需要分配的字符长度(*nul 终止字符的长度+1)。然后只需分配,分配给您的新指针,并从固定缓冲区复制到单词(或行)的新存储块。A 1K、2K 等。固定缓冲区很好。

因此,让我们收集代码所需的变量,为固定缓冲区大小定义一个常量,例如

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024       /* if you need a constant, #define one (or more) */
int main (void)
{
char **arr = NULL,      /* pointer to block of allocated pointers */
buf[MAXC];          /* fixed buffer for read-input */
int size_arr = 0;       /* only 1 counter needed here */

现在让我们读出一行buf,然后从分配指针开始:

while (fgets (buf, MAXC, stdin))
{   
size_t len;

/* allocate pointer (one each time rather inefficient) */
void *tmp = realloc (arr, (size_arr + 1) * sizeof *arr);
if (!tmp) {                                 /* VALIDATE */
perror ("realloc-arr");
break;
}
arr = tmp;                                  /* assign on success */

(如我的评论中所述,您永远不会realloc()使用指针本身,因为当(不是如果)realloc()失败时,它会用NULL覆盖指针地址(例如指向指针集合的地址)。

所以上面,你realloc()到临时指针tmp,验证重新分配是否成功,然后将新分配的指针块分配给arr

现在从buf修剪'n'并获取字符数。(其中strcspn()允许您在一次调用中完成所有操作):

buf[(len = strcspn (buf, "n"))] = 0;       /* trim n, save len */

现在只需为len + 1字符分配存储空间并从buf复制到arr[size_arr]

arr[size_arr] = malloc (len + 1);           /* allocate for word */
if (!arr[size_arr]) {                       /* VALIDATE */
perror ("malloc-arr[i]");
break;
}
memcpy (arr[size_arr], buf, len + 1);       /* copy buf to arr[i] */
size_arr += 1;                              /* increment counter */
}

(注意:每次迭代重新分配 1 个指针时,只需要一个计数器变量,请注意,在指针重新分配、Word 存储分配验证并将 Word 从buf复制到arr[size_arr]之前,它不会递增。在任一分配失败时,循环将被破坏,您的size_arr仍将保存正确数量的存储字)

这样就完成了您的阅读循环。

现在,您可以使用存储的size_arr指针集合,每个指针都指向您愿意分配和存储的单词。但请记住,当需要释放内存时,这也是一个两步过程。在释放分配指针的块之前,您必须释放每个单词的分配块,例如

for (int i = 0; i < size_arr; i++) {            /* output result */
puts (arr[i]);
free (arr[i]);                              /* free word storage */
}
free(arr);                                      /* free pointers */

做。

完整的程序是:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024       /* if you need a constant, #define one (or more) */
int main (void)
{
char **arr = NULL,      /* pointer to block of allocated pointers */
buf[MAXC];          /* fixed buffer for read-input */
int size_arr = 0;       /* only 1 counter needed here */

while (fgets (buf, MAXC, stdin))
{   
size_t len;

/* allocate pointer (one each time rather inefficient) */
void *tmp = realloc (arr, (size_arr + 1) * sizeof *arr);
if (!tmp) {                                 /* VALIDATE */
perror ("realloc-arr");
break;
}
arr = tmp;                                  /* assign on success */

buf[(len = strcspn (buf, "n"))] = 0;       /* trim n, save len */

arr[size_arr] = malloc (len + 1);           /* allocate for word */
if (!arr[size_arr]) {                       /* VALIDATE */
perror ("malloc-arr[i]");
break;
}
memcpy (arr[size_arr], buf, len + 1);       /* copy buf to arr[i] */
size_arr += 1;                              /* increment counter */
}

for (int i = 0; i < size_arr; i++) {            /* output result */
puts (arr[i]);
free (arr[i]);                              /* free word storage */
}
free(arr);                                      /* free pointers */
}

示例使用/输出

测试一下。做一些有创意的事情,比如读取、存储和输出你的源文件,以确保它正常工作,例如

$ ./bin/dynarr < dynarr.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024       /* if you need a constant, #define one (or more) */
int main (void)
{
char **arr = NULL,      /* pointer to block of allocated pointers */
buf[MAXC];          /* fixed buffer for read-input */
int size_arr = 0;       /* only 1 counter needed here */
while (fgets (buf, MAXC, stdin))
{
size_t len;
/* allocate pointer (one each time rather inefficient) */
void *tmp = realloc (arr, (size_arr + 1) * sizeof *arr);
if (!tmp) {                                 /* VALIDATE */
perror ("realloc-arr");
break;
}
arr = tmp;                                  /* assign on success */
buf[(len = strcspn (buf, "n"))] = 0;       /* trim n, save len */
arr[size_arr] = malloc (len + 1);           /* allocate for word */
if (!arr[size_arr]) {                       /* VALIDATE */
perror ("malloc-arr[i]");
break;
}
memcpy (arr[size_arr], buf, len + 1);       /* copy buf to arr[i] */
size_arr += 1;                              /* increment counter */
}
for (int i = 0; i < size_arr; i++) {            /* output result */
puts (arr[i]);
free (arr[i]);                              /* free word storage */
}
free(arr);                                      /* free pointers */
}

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有 2个责任:(1)始终保留指向内存块起始地址的指针,以便 (2) 当不再需要内存块时可以释放它。

必须使用内存错误检查程序来确保不会尝试访问内存或超出/超出分配块边界的写入,不会尝试读取或基于未初始化值的条件跳转,最后确认释放了已分配的所有内存。

对于Linux来说,valgrind是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。

$ valgrind ./bin/dynarr < dynarr.c
==30995== Memcheck, a memory error detector
==30995== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==30995== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==30995== Command: ./bin/dynarr
==30995==
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
<snipped code>
}
==30995==
==30995== HEAP SUMMARY:
==30995==     in use at exit: 0 bytes in 0 blocks
==30995==   total heap usage: 84 allocs, 84 frees, 13,462 bytes allocated
==30995==
==30995== All heap blocks were freed -- no leaks are possible
==30995==
==30995== For counts of detected and suppressed errors, rerun with: -v
==30995== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存,并且没有内存错误。

仔细查看,如果您有其他问题,请告诉我。

相关内容

  • 没有找到相关文章

最新更新