[英]Char Arrays and Segmentation Fault
新的C和指针,我无法弄清楚为什么我在这里有一个分段错误...这里的一些代码是未使用的,我只是想测试我的单词是否正在被正确读取并且有分配的空间量正确。 最终我会有一个char **,其中打印时的格式看起来像“cat the”,“new hey”,.....
在我的代码中,单词代表每个单词
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<ctype.h>
char *expand(char* source, int size);
char *expand(char *source, int size)
{
char *expansion = (char *)malloc(size * sizeof(char));
int i;
for (i=0; i<size-1; i++)
{
expansion[i] = source[i];
}
free(source);
return expansion;
}
int main(int argc, char **argv)
{
int x;
if (argc <= 2)
{
if (isdigit(*argv[1]))
{
x = atoi(argv[1]);
}
}
else
{
fprintf(stderr, "Invalid Input\n");
return 1;
}
//store pointers to each individual word
char *words = (char *)malloc(3 * sizeof(char));
int size = 3;//initial arbitrary size
int count = 0;
char *temp;
while (1)
{
fscanf(stdin, "%s", temp);
if (feof(stdin))
{
break; //break if end of file
}
if (count == size - 1)
{
size++;
words = expand(words, size);
}
words[count] = *temp;
count++;
}
int i;
for(i=0; i<size-1; i++)
{
fprintf(stderr, "%s, ", words[i]);
}
fprintf(stderr, "\n");
return 0;
}
你跳出来的麻烦的一个主要原因是你声明了char *temp
; 然后,在你的while循环中,开始尝试将数据读入指针所指向的内存中...但是,你从不打扰实际分配内存并将指针指向它。
这是一个非常理想的示例,您应该使用它来修改char *words
和char *temp
。 我建议,在您熟悉如何正确管理动态内存(malloc / free)之前,请先使用静态声明:
char words[512];
int size = 3;
int count = 0;
char temp[512];
words[0] = 0;
temp [0] = 0;
while...
虽然这样的“幻数”声明显然不明智或不安全,但它们使用数字(如512)创建一个起始点,该数字可能比您在测试时可能传递的任何基于行的输入更大。 一旦你正确地工作,然后再回来看看malloc
!
考虑这个为argv[1]
传递的理论参数分配内存的例子:
char *ptr = NULL;
ptr = malloc(strlen(argv[1] + 1);
strcpy(ptr, argv[1]);
... Do lots of stuff ...
free(ptr);
请注意,分配大于为字符串的空终止符留出空间所需的字符串长度。
您可能感兴趣的还有strdup
函数,它将为您执行正确的分配,但您必须记住在通过时free
内存。
您的直接分段错误是由于:
char *temp;
...
fscanf(stdin, "%s", temp)
其中temp
是未分配的字符指针。 声明指针时,例如char *temp;
指针不指向任何东西。 它只是一个空变量。 对于指向函数的指针,您希望temp
工作,它必须将地址保存为足够大小的内存块作为其值。
这样想吧。 当你声明int i;
,在为其分配值之前,您不希望i
保留任何特定值。 'i'
未初始化 。 指针也不例外。 你不会指望char *temp;
在指定有效地址之前指向任何内容。 当你调用fscanf
并尝试将输入存储在temp
- boom ,segfault所指向的地址时,因为没有告诉临界temp
指向的位置。 它当然不是一个足够大小的分配内存块。
除了当前的问题之外,很难遵循代码的逻辑,因为它在推理中非常混乱。 猜测,看来你的目标是要通过值x
命令行,然后最终使用该值size
来expand
'size'
从读单词的数量stdin
。 相反,它看起来像你被嘲笑,只是分配size = 3;
并决定尝试让它为3个单词工作。 没有必要放弃,正确地做,不需要更多的努力。
将作为字符串输入的数字转换为argv[1]
,可以使用该值创建多个指针的可变长度数组。 没有任何令人信服的理由来声明一些任意数量的指针,因为你所做的输入可能会告诉你你希望扩展多少个单词。 此时也无需为指针分配存储空间,因为这似乎是expand
的目的。
你正在读stdin的话 。 只要它们是普通单词,您就可以简单地使用静态声明的缓冲区来保存从stdin
读取的单词。 只要它们是字典中的单词,您就知道它们不会超过28-characters
。 所以你需要不超过29-characters
( nul-terminating字符为+1
)来保存每个单词作为你的阅读。 [1]
scanf
系列函数根据给定的格式字符串返回成功转换的次数。 您不使用feof
查找输入的结尾,检查fscanf
的返回并限制处理size
的单词数。 要清理处理输入的方式(我已使用x
size
),您可以执行类似以下操作:
enum { MAXC = 32 }; /* constant for static buffer - longest word 28 char */
...
int main (int argc, char **argv) {
...
int size; /* validate numeric input */
if ((size = isdigit (*argv[1]) ? atoi (argv[1]) : 0) <= 0) {
fprintf (stderr, "error: invalid input. usage %s int > 0\n", argv[0]);
return 1;
}
...
int count = 0, i = 0;
char *words[size]; /* array of 'size' pointers to char */
char temp[MAXC] = {0}; /* buffer to hold each word input */
/* read at most 'size' words from stdin */
while (size-- && fscanf (stdin, "%s", temp) == 1)
words[count++] = expand (temp, strlen (temp)); /* expand */
查看声明的每个部分并阅读循环。 (了解在设置size
时使用三元运算符 - 在许多情况下它是一个有用的快捷方式)注意你有两个条件,读取size > 0
和fscanf (stdin, "%s", temp) == 1
。 如果您读取size
或没有其他输入要读取,则循环终止。
清理的其余部分相当直接。 但请注意,不需要使用variadic fprintf
简单地将换行符打印到stderr
(例如fprintf (stderr, "\\n");
)。 只需打印单个字符(例如fputc ('\\n', stderr);
)。
此外,无论何时分配内存,都由您决定(1)保留指向内存块开头的指针,以便在不再需要时可以(2)释放它。 始终,始终在Linux上使用像valgrind
这样的内存/错误检查程序来验证您是否正确使用了内存,并且在不再需要时已释放所有块。 每个操作系统都有类似的程序,它们易于使用。 没有理由不去。
将所有部分组合在一起,您可以执行以下操作:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
enum { MAXC = 32 }; /* constant for static buffer - longest word 28 char */
char *expand (char *source, size_t size);
int main (int argc, char **argv) {
if (argc != 2) { /* validate number of arguments */
fprintf (stderr, "error: insufficient input.\n");
return 1;
}
int size; /* validate numeric input */
if ((size = isdigit (*argv[1]) ? atoi (argv[1]) : 0) <= 0) {
fprintf (stderr, "error: invalid input. usage %s int > 0\n", argv[0]);
return 1;
}
int count = 0, i = 0;
char *words[size]; /* array of 'size' pointers to char */
char temp[MAXC] = {0}; /* buffer to hold each word input */
/* read at most 'size' words from stdin */
while (size-- && fscanf (stdin, "%s", temp) == 1)
words[count++] = expand (temp, strlen (temp)); /* expand */
for (i = 0; i < count; i++) { /* output each string read */
char *fmt = i ? ", %s" : "%s";
fprintf (stderr, fmt, words[i]);
}
fputc ('\n', stderr);
for (i = 0; i < count; i++) /* free allocated memory */
free (words[i]);
return 0;
}
char *expand (char *source, size_t size)
{
char *expansion = calloc (1, size * sizeof *expansion + 1);
size_t i;
if (!expansion) { /* validate memory allocation */
fprintf (stderr, "error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
for (i = 0; i < size; i++)
expansion[i] = source[i];
return expansion;
}
(注意:上面使用了calloc
来避免某些valgrind
版本中的怪癖。根据版本,它可能会抱怨在if(..)
语句中未初始化地使用expansion
。这不是错误,而是一个怪癖为了确保你没有遇到这个问题,我使用了calloc
而不是malloc
,它将所有新内存初始化为零并避免警告。 注意:它还确保expansion
没有显式expansion[size] = 0;
nul-termination expansion[size] = 0;
循环之后。)
示例输入
$ cat ../dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
产量
$ ./bin/expansion 5 < ../dat/captnjack.txt
This, is, a, tale, Of
$ ./bin/expansion 4 < ../dat/captnjack.txt
This, is, a, tale
内存/错误检查
$ valgrind ./bin/expansion 5 < ../dat/captnjack.txt
==12849== Memcheck, a memory error detector
==12849== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==12849== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==12849== Command: ./bin/expansion 5
==12849==
This, is, a, tale, Of
==12849==
==12849== HEAP SUMMARY:
==12849== in use at exit: 0 bytes in 0 blocks
==12849== total heap usage: 5 allocs, 5 frees, 18 bytes allocated
==12849==
==12849== All heap blocks were freed -- no leaks are possible
==12849==
==12849== For counts of detected and suppressed errors, rerun with: -v
==12849== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
如果您有任何疑问,请告诉我。 我试着尽可能地猜测你在哪里使用你的代码。 如果我错过了标记,请告诉我,我很乐意进一步提供帮助。
脚注1. - “ Antidisestablishmentarianism ”是未删节词典中最长的词。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.