创建一个动态数组,但将分段错误作为错误
Creating an dynamic array, but getting segmentation fault as error
我想创建一个包含用户输入的动态数组。但是在我第一次输入后,我一直收到分段错误作为错误。我知道分段错误是由于错误的内存访问引起的。有没有办法定位代码中的错误?
#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;
}
您好,
索尔塞斯
你很接近,你只是对如何处理将所有部分放在一起有点困惑。首先,了解您的代码中不涉及任何数组。当您使用 pointer-to-pointer(例如 char **arr
)构建 collection 时,这是一个两步过程。 arr
本身是指向分配的指针块的 single-pointer -- 您每次将其扩展一个以通过调用 realloc()
重新分配一个额外的指针来添加下一个单词。
第二步是为每个单词分配存储空间。然后,您将该字存储的起始地址分配给您分配的指针。
所以你有一个指针块,你可以扩展 (realloc()
) 添加一个指针来保存下一个单词的地址,然后你为那个单词分配存储空间,将起始地址分配给你的新单词指针,然后将单词复制到该地址。
(注意: 调用 realloc()
每次迭代只添加 one-pointer 是低效的。你可以通过添加另一个计数器来解决这个问题(例如 allocated
) 保存分配的指针数,以及一个计数器 used
,以跟踪您使用的指针数。您仅在 used == allocated
时重新分配。这样您就可以,例如将数字加倍每次可用的指针数量——或您选择的任何增长方案)
另请注意,strdup()
和 strndup()
很方便,但不是标准 C 库的一部分(它们是 POSIX)。虽然大多数编译器都会提供它们,但您可能需要正确的编译器选项以确保它们可用。
让我们看看你的例子,在简单的情况下,只使用标准库提供的函数。我们将让您通过一种方案重新分配,并让您实施 used == allocated
方案以稍后清理它。
读取数据行时,在读取该行之前您不会知道需要存储多少个字符——因此只需重复使用相同的 fixed-size 缓冲区来读取每一行。然后你可以 trim fgets()
包含的 '\n'
并获得你需要分配的字符的长度(+1
为 *nul-terminating 字符)。然后简单地分配,分配给你的新指针并从固定缓冲区复制到单词(或行)的新存储块。 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()
失败时,它将覆盖指针地址(例如您的 collection 个指针)和 NULL
。)
所以上面,你realloc()
到临时指针tmp
,验证重新分配成功,然后将新分配的指针块分配给arr
。
现在 trim 来自 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 个指针时 per-iteration,只需要一个计数器变量,注意它是如何在指针重新分配之前不递增的,您的单词存储分配已验证,单词从 buf
复制到 arr[size_arr]
。如果任一分配失败,循环将被破坏,您的 size_arr
仍将保持正确的数量存储词)
那使你完整 read-loop。
现在您可以使用已存储的 collection 个指针,共 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 */
}
示例Use/Output
测试一下。做一些有创意的事情,比如读取、存储和输出你的源文件,以确保它能正常工作,例如
$ ./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 */
}
内存Use/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于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)
始终确认您已释放所有内存您已经分配并且没有内存错误。
检查一下,如果您还有其他问题,请告诉我。
我想创建一个包含用户输入的动态数组。但是在我第一次输入后,我一直收到分段错误作为错误。我知道分段错误是由于错误的内存访问引起的。有没有办法定位代码中的错误?
#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;
}
您好, 索尔塞斯
你很接近,你只是对如何处理将所有部分放在一起有点困惑。首先,了解您的代码中不涉及任何数组。当您使用 pointer-to-pointer(例如 char **arr
)构建 collection 时,这是一个两步过程。 arr
本身是指向分配的指针块的 single-pointer -- 您每次将其扩展一个以通过调用 realloc()
重新分配一个额外的指针来添加下一个单词。
第二步是为每个单词分配存储空间。然后,您将该字存储的起始地址分配给您分配的指针。
所以你有一个指针块,你可以扩展 (realloc()
) 添加一个指针来保存下一个单词的地址,然后你为那个单词分配存储空间,将起始地址分配给你的新单词指针,然后将单词复制到该地址。
(注意: 调用 realloc()
每次迭代只添加 one-pointer 是低效的。你可以通过添加另一个计数器来解决这个问题(例如 allocated
) 保存分配的指针数,以及一个计数器 used
,以跟踪您使用的指针数。您仅在 used == allocated
时重新分配。这样您就可以,例如将数字加倍每次可用的指针数量——或您选择的任何增长方案)
另请注意,strdup()
和 strndup()
很方便,但不是标准 C 库的一部分(它们是 POSIX)。虽然大多数编译器都会提供它们,但您可能需要正确的编译器选项以确保它们可用。
让我们看看你的例子,在简单的情况下,只使用标准库提供的函数。我们将让您通过一种方案重新分配,并让您实施 used == allocated
方案以稍后清理它。
读取数据行时,在读取该行之前您不会知道需要存储多少个字符——因此只需重复使用相同的 fixed-size 缓冲区来读取每一行。然后你可以 trim fgets()
包含的 '\n'
并获得你需要分配的字符的长度(+1
为 *nul-terminating 字符)。然后简单地分配,分配给你的新指针并从固定缓冲区复制到单词(或行)的新存储块。 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()
失败时,它将覆盖指针地址(例如您的 collection 个指针)和 NULL
。)
所以上面,你realloc()
到临时指针tmp
,验证重新分配成功,然后将新分配的指针块分配给arr
。
现在 trim 来自 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 个指针时 per-iteration,只需要一个计数器变量,注意它是如何在指针重新分配之前不递增的,您的单词存储分配已验证,单词从 buf
复制到 arr[size_arr]
。如果任一分配失败,循环将被破坏,您的 size_arr
仍将保持正确的数量存储词)
那使你完整 read-loop。
现在您可以使用已存储的 collection 个指针,共 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 */
}
示例Use/Output
测试一下。做一些有创意的事情,比如读取、存储和输出你的源文件,以确保它能正常工作,例如
$ ./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 */
}
内存Use/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于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)
始终确认您已释放所有内存您已经分配并且没有内存错误。
检查一下,如果您还有其他问题,请告诉我。