分解循环的结束条件

时间:2012-06-07 18:01:12

标签: c strtok factors strtol

晚上好,

我遇到了分配问题。

基本上我们需要对程序进行编码,该程序将计算给定stdin的素因子。数据只能通过stdin进入程序,无论是echo还是< file.txt。数据流永远不会超过80个字符(它们可以是数字或不是数字)。

我在程序中使用的函数是read()strotol()strtok(),“无关”代码的流程如下:

  1. 使用malloc分配80个初始字节的内存。
  2. int存储,通过read()读取的字符数(我相信,最后\0)。
  3. 使用realloc()重新分配内存,以便节省尽可能多的内存(我知道在这种情况下它很简单,但是很好......)。
  4. 现在有点棘手:

    1. 由于数据必须用空格分隔,因此要检查的最大项目数量最多为:(n/2)+1,其中n是在第2点上方读取的字符数。< / LI>
    2. 创建一个long数组,其最大大小为在第nº1点获得的数字。
    3. 填写numbers[0],结果为:strtol(strtok(line, delim), &end, 10)
    4. 1添加到counter并输入while循环:

      while((numbers[counter] = strtol(strtok(NULL, delim), &end, 10)) != NULL) {
          if(!*end) {
              // Check whether it's 0, 1, negative, prime or extract its factors
          }
          else {
              fprintf(stderr, "\"%s\" is not a non-negative integer", end)
          }
          counter++;
      }
      
    5. 现在,这里有一些输入及其输出:

      输入:echo 1 2 3 4 5 6 7 8 9 10 | ./factors

      输出:

      1
      2    
      3
          2    2
      5
          2    3
      7 
          2    2    2
          3    3
          2    5
      Segmentation Fault (core dumped). 
      

      输入./factors < integers.txt 其中整数包含整数列。

      输出:

      所有整数都被分解得很好,最后它打印出一个:

      Segmentation Fault (core dumped). 
      

      输入:echo abc 12 13 14 15 | ./factors

      输出:

      "abc" is not a non-negative integer
      13
          2    7
          3    5
      Segmentation Fault (core dumped). 
      

      输入:echo -1 -2 -3 -4 -5 | ./factors

      输出:

      "-1" is not a non-negative integer
      "-2" is not a non-negative integer
      "-3" is not a non-negative integer
      "-4" is not a non-negative integer
      "-5" is not a non-negative integer
      

      输入:echo abc abc abc | ./factors

      输出:

      "abc" is not a non-negative integer
      

      (并且不继续检查)。

      输入:echo 3 4 0 6 7 | ./factors

      输出:

      3
          2    2
      

      (并且不继续检查)。

      据我所知,当它遇到0,非integer的多个实例或基本上在健康的integer结束时,它会失败 - 基于数据流。

      任何想法我怎么能解决这个问题,为什么它显然随机失败?

      我应该让你知道我是C的新手......

      非常感谢您的进步。

      =============================================== =====

      EDIT1:根据请求,以下是生成numbers[]并从stdin读取的代码片段:

      char *line;
      char *end;
      char *delim = " \n";
      int charsread, counter; 
      
      line = malloc(80);
      charsread = read(0, line, 81);
      if (charsread == 0) {
          return EX_OK;
      }
      realloc(line, charsread);
      maxelem = (charsread / 2) + 1;
      long numbers[maxelem];
      numbers[0] = strtol(strtok(line, delim), &end, 10);
      if (!*end) {
          zeroone(numbers[0]);
      }
      else {
          fprintf(stderr, "\"%s\" is not a non-negative integer\n", end);
      }
      counter = 1;
      while [...]
      

3 个答案:

答案 0 :(得分:2)

尝试使用gdb调试segfault,通过设置适当的环境变量使其在segfaulting时转储核心,或者直接在gdb中运行它。 Segfault意味着您正在读/写一部分不应该的内存。随机性意味着你可能正在粉碎堆栈或其他东西。我认为“printf”应该受到指责,检查他们的论点。您也没有检查数字是否小于数组长度?它可能会超支吗?

答案 1 :(得分:1)

好的,让我们回顾一下我们在那里的一些事情,看看我们是否能解决你的问题。


您的计划不需要这样做:

realloc(line, charsread);

尝试减少内存占用是正确的,但是如果你分配的太多,那么呢?这是80个字节。不要过分复杂,你的盘子已经足够了。


这很奇怪:

maxelem = (charsread / 2) + 1;
long numbers[maxelem];

这很好,可以在您的情况下使用,但您可以通过计算数字组来更准确地确定元素的数量。


我建议尝试在lopp中完成整个提取


关于实际的segmentation fault ...我可能错了,因为我没有机会在我的机器上重现这一点,但我认为错误的发生是因为你没有终止你的line具有\0字符。在C中,因为我确定你已经知道你发布的片段的外观,行通常是我们所谓的“以NULL结尾”,所以按照惯例,我们用值0来终止它们,这是0的值。 NUL字符,也表示为\0(有时候有点令人困惑,因为人们会在“NULL-terminated”,“NUL”字符和“NULL指针”之间混淆,另一件事。)

这会使你的程序中断,因为最终你的程序试图用strtok读取行的结尾,但它不知道在哪里停止。它没有缓冲区长度的良心以及在此缓冲区中停止的位置。所以它不断读取,到达不允许访问的内存地址,因此出现segmentation fault

所以你想简单地说:

/*
** If you keep your realloc, you need to allocate for the number of read
** characters from stdin, and for an extra char to terminate.
*/
line = realloc(line, charsread + 1); 
/*
** Terminate the string.
*/
line[charsread] = '\0';

更新:啊,你实际上差不多了,你有逻辑,但可能只是错过了这一点......你甚至自己写了这个:

  

在int中存储,通过read()读取的字符数(我相信,最后的\ 0)。

部分正确。如果您确实获得了80个字符的行,那么您的read调用将返回最后一行\0的行。但是大部分时间你都没有,所以你的读缓冲区只能读取可见字符,你需要自己对字符串进行空终止。


我还会尝试重写你的处理循环,以便对strtok进行第一次初始调用。写入并不总是很方便,但通常看到一个代码块几乎与循环内部或循环之后的内部相同,这让我觉得有更好的逻辑方法。

答案 2 :(得分:0)

非常感谢您的深刻回应。

最后我只重写了整个事情,因为它看起来并不像我预期的那么干净。 由于strtok的手册指定了NULL的返回值,如果无法提取令牌,这就是我最终的结果:

long number;
item = strtok(line, delim);
while (item != NULL) {
       number = strtol(item, &rest, 10);
       if (*rest == 0) {
           zeroOne(number);
       }
       else {
           fprintf(stderr, "\"%s\": not a non-negative int.\n", item);
       }
       item = strtok(NULL, delim);
}   

似乎是一种更清晰的方法,并且在尝试进入之前的NULL循环之前,确实会考虑返回strtok返回的值while的事实。然后,今天早上我回到这里阅读你的回复:)

关于您对该行的\0的硬编码输入的后续问题:这是否意味着我的上一个strtok实际输出\0令牌并尝试输入循环,还是在我最后一个引入的角色后到达实体时返回NULL? 作为惯例,当使用read()(或者可能是其他读取函数,例如fgets()时,我应该总是尝试将\0硬编码到读取行以允许其他函数检查{ {1}} / EOL

如果其他人在使用上述任何功能时遇到问题(EOFstrtok),我建议您查看本网站上发布的这两个问题:

关于strtol的第二个论点:Strtol second argument

关于strtol的输出:Cannot concatenate strtok's output variable. strcat and strtok