Char数组和分段错误

时间:2016-02-14 22:35:05

标签: c arrays pointers char 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;
}

2 个答案:

答案 0 :(得分:2)

你跳出来的麻烦的一个主要原因是你声明char *temp;然后,在你的while循环中,开始尝试将数据读入指针所指向的内存中...但是,你从不打扰实际分配内存并将指针指向它。

这是一个非常理想的示例,您应该使用它来修改char *wordschar *temp。我建议,在您熟悉如何正确管理动态内存(malloc / free)之前,请先从静态声明开始:

char words[512];
int size = 3;
int count = 0;
char temp[512];
words[0] = 0;
temp [0] = 0;
while...

同时&#34;幻数&#34;这样的声明显然不明智或安全,它们使用一个数字(如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内存。

答案 1 :(得分:1)

您的即时分段错误原因是:

char *temp;
...
fscanf(stdin, "%s", temp)

其中temp未分配的字符指针。声明指针时,例如char *temp;指针不会指向任何东西。它只是一个空变量。对于指向您希望temp起作用的函数的指针,它必须将地址保存为一个足够大小的内存块作为其值。

这样想。当您声明int i;时,在您为其指定值之前,不要期望i保留任何特定值。 'i' 未初始化。指针也不例外。在您为其分配有效地址之前,您不会指望char *temp;指向任何内容。当您致电fscanf并尝试将输入存储在temp - 热潮所指向的地址时,会发生段错误,因为无法确切地说明temp指着。它当然不是一个足够大小的分配内存块。

除了当前的问题之外,很难遵循代码的逻辑,因为它在推理中非常混乱。猜测,您的目标似乎是从命令行传递x的值,然后最终将size的值用于expand 'size'从中读取的单词数量stdin。相反,它看起来像你被抨击并且刚刚分配size = 3;并且决定尝试让它为3个单词工作。没有必要放弃,正确地做,不需要更多的努力。

将作为字符串输入的数字argv[1]转换为整数时,可以使用该值创建多个指针的可变长度数组。没有任何令人信服的理由来声明一些任意数量的指针,因为你所做的输入可能会告诉你你希望扩展多少个单词。此时也无需为指针分配存储空间,因为这似乎是expand的目的。

您正在从标准输入读取单词。只要它们是普通单词,您就可以简单地使用静态声明的缓冲区来保存从stdin读取的单词。只要它们是字典中的单词,您就知道它们不会超过28-characters。因此,对于 nul-terminatedating 字符,您只需要29-characters+1)即可将每个单词保留为读取它。 [1]

scanf系列函数根据给定的 format-string 返回成功转换的次数。您不使用feof查找输入结束,检查fscanf的返回值并将处理的字数限制为size。要清理处理输入的方式(我已使用size作为x),您可以执行类似以下操作:

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和{{提供2个条件1}}。如果您阅读fscanf (stdin, "%s", temp) == 1或没有其他要读取的输入,则循环终止。

清理的其余部分相当直接。但请注意,不需要使用可变参数size来简单地将换行符打印到fprintf(例如stderr)。只需打印单个字符(例如fprintf (stderr, "\n");)。

此外,无论何时分配内存,都 (1)保留指向内存块开头的指针,因此它可以(2)在不再需要时释放。始终,始终在Linux上使用像fputc ('\n', stderr);这样的内存/错误检查程序来验证您是否正确使用了内存,并且在不再需要时已释放所有块。每个操作系统都有类似的程序,它们易于使用。没有理由不去。

将所有部分组合在一起,您可以执行以下操作:

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中未expansion的未初始化使用这不是一个错误,但在旧版本中是一个怪癖。为了确保你没有遇到这个问题,我使用if(..)而不是calloc将所有新内存初始化为零并避免警告。注意:它还会确保malloc nul-termination ,而不会在循环后显示expansion 。)

示例输入

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

如果您有任何疑问,请与我们联系。我试着尽可能地猜测你在哪里使用你的代码。如果我错过了标记,请告诉我,我很乐意进一步帮助。

脚注1。 - &#39; Antidisestablishmentarianism &#39;是未删节字典中最长的单词。