【问题标题】:C—Infinite loop, I think?C——我认为是无限循环?
【发布时间】:2014-03-20 14:09:53
【问题描述】:

我在使用 C 语言的程序时遇到问题,我认为 for 循环是罪魁祸首,但我不确定。该函数旨在获取已经通过反向函数传递的char[],并将其写入另一个char[],并删除所有尾随空格字符。也就是说,位于'\n' 和任何其他字符之间的任何' ''\t' 字符都不应成为输出的一部分。

如果没有尾随空格字符,它就可以完美地工作,就像重写输入 char[] 的精确副本一样。但是,如果有的话,根本就没有输出。

程序如下:

#include<stdio.h>

#define MAXLINE 1000

void trim(char output[], char input[], int len);
void reverse(char output[], char input[], int len);

main()
{
    int i, c;
    int len;

    char block[MAXLINE];
    char blockrev[MAXLINE];
    char blockout[MAXLINE];
    char blockprint[MAXLINE];

    i = 0;
    while ((c = getchar()) != EOF)
    {
        block[i] = c;
        ++i;
    }

    printf("%s", block); // for debugging purposes
    reverse(blockrev, block, i); // reverses block for trim function
    trim(blockout, blockrev, i);
    reverse(blockprint, blockout, i); // reverses it back to normal
    // i also have a sneaking suspicion that i don't need this many arrays?
    printf("%s", blockprint);
}

void trim(char output[], char input[], int len)
{
    int i, j;

    i = 0;
    j = 0;

    while (i <= len)
    {
        if (input[i] == ' ' || input[i] == '\t')
        {
            if (i > 0 && input[i-1] == '\n')
                for (; input[i] == ' ' || input[i] == '\t'; ++i)
                {

                }
            else
            {
                output[j] = input[i];
                ++i;
                ++j;
            }
        }

        else
        {
            output[j] = input[i];
            ++i;
            ++j;
        }
    }
}

void reverse(char output[], char input[], int len)
{
    int i;

    for (i = 0; len - i >= 0; ++i)
    {
        output[i] = input[len - i];
    }
}

我应该注意到这是一个不允许使用字符串函数的类分配,因此它是如此迂回。

【问题讨论】:

  • 这就是为什么我喜欢匹配括号在同一列中的阶梯
  • 或者只是任何一致的缩进样式。看起来你一开始在这里有while/else
  • 整理出缩进 - 然后也许能够发现问题。您是否考虑过使用调试器?
  • 稍微清理了一下。对不起,这只是我的第二个帖子,我是一个 C 新手。不过现在应该更容易阅读了。
  • 你确定你得到一个无限循环吗?看起来你会在for (i; input[i] == ' ' || input[i] == '\t'; ++i);附近遇到分段错误

标签: c arrays function loops infinite-loop


【解决方案1】:

改变

for (i; input[i] == ' ' || input[i] == '\t'; ++i);

for (; i <= len && (input[i] == ' ' || input[i] == '\t'); ++i);

使用第一种方法,如果空格在末尾,则循环将无限迭代。不知道你是如何没有得到越界访问异常的,但那是你的 C/C++。

编辑正如 Arkku 在 cmets 中提出的,确保您的字符数组仍然是 NUL 终止的(\0 字符),您可以检查这种情况。确保您也没有从末尾修剪 NUL 字符。

【讨论】:

  • 嗯,如果 input 是 NUL 终止的,检查 len 应该没有区别,因为结尾不匹配空格或制表符? (当然,OP 没有指定 input 是否为 C 字符串。)
  • C 没有运行时边界检查。它的“魅力”之一。如果你有耐心,你最终会遇到某种访问冲突/分段错误。
  • @Arkku:关于 NUL 终止的要点。也许 OP 也没有在预处理反向步骤中将标记字符添加到数组中。
  • @Arkku - 是的,如果它真的是字符数据,你真的需要 nul 终止它。它不是语言的要求,但大量的字符串处理库例程(和用户)会期望它。违背约定只是自找麻烦。
【解决方案2】:

简单地将main() 函数声明为main() 是一种过时的样式,不应使用。该函数必须声明为int main(void)int main(int argc, char *argv[])

您的输入过程不会以空值终止您的输入。这意味着您正在使用的不是“字符串”,因为根据定义,C 字符串是char 的数组,最后一个元素是空字符('\0')。相反,你得到的是char 的简单数组。只要您期望,这不会是一个问题,并且确实您的代码正在传递数组长度,但您也尝试使用printf() 打印它,这需要 C 字符串,而不是简单的 char 数组.

您的 reverse() 函数有一个非一的错误,因为您没有考虑到 C 数组是零索引的事实,所以您反转的总是比实际输入长一个字节。

这意味着如果您调用reverse(output, input, 10),您的代码将首先将input[10] 的值分配给output[0],但input[10] 是您的结束的一个过去数据,并且由于您在开始填充数组之前没有初始化数组,因此这是一个不确定的值。在我的测试中,巧合的是,在我的测试中,这个不确定的值大部分时间都是零值,这意味着output[0] 被填充为空值 ('\0')。

您需要在input 的索引中减去比实际值多一个。 reverse() 函数中的循环终止条件也是错误的,作为补偿,该条件应该是len - i &gt; 0,而不是len - i &gt;= 0

您的trim() 函数过于复杂。此外,它也有一个不正确的循环条件来补偿reverse() 中的非一错误。循环应该是while ( i &lt; len ),而不是while ( i &lt;= len )

此外,trim() 函数能够减小数据大小,但您不提供保留该信息的方法。 (我在 Arkku 的回答中看到您已经对此进行了纠正。很好。)

一旦您解决了不跟踪数据大小更改的问题,以及从 @ 末尾复制不确定数据(巧合的是,该错误为空)的逐一错误当您执行第二个 reverse() 时,将 987654344@ 数组添加到 blockprint 数组的开头,并且您修复了 trim() 中不正确的 &lt;= 条件和 reverse() 中不正确的 &gt;= 条件,并且您为 null-在将字节数组传递给printf() 之前终止它,您的程序将运行。

【讨论】:

  • 关于trim,在我看来,OP 的循环条件i &lt;= len 实际上具有正确NUL 终止输出的效果。不过,我同意,最好只迭代字符串本身然后手动终止……(另外,我认为最好通过其三字母助记符来引用 NUL 字符,而不是“null”,由于 C 中的 NULL 指的是空指针。)
  • 无论如何,+1 表示注意到 OP 修改后的问题中的其他问题(原始问题仅显示了 trim 函数)。
  • @Arkku 如何正确以空值终止输出?那个 null 是从哪里来的? (提示:“不确定”。)
  • 如果triminput 被终止,并且循环按照OP 的帖子(即i &lt;= len),那么来自input[len] 的终止NUL 被分配到@ 的末尾据我所知,在每种情况下都是 987654360@。 (您正确地发现,在将字符串传递给trim 之前,OP 的代码不会确定性地终止字符串,但如果在main 中修复了这个问题,那么在它运行的前提条件下,trim 似乎是正确的C 字符串和len 是根据strlen 的长度。)
  • @Arkku ...很好。 IF 字符串以正确的空终止开头,并且 IF 修复了reverse() 函数,然后trim() 中不正确的循环条件会意外产生影响以空值结尾的缩短字符串。不过,它仍然
【解决方案3】:

(从 cmets 移至答案)

我的猜测是问题在这个函数之外,并且是由于在所描述的问题情况下输出比输入短的事实引起的。由于您将字符串的长度作为参数传递,因此您需要计算trim之后的字符串长度,因为它可能已经改变了......

例如,将不正确的长度传递给 reverse 可能会导致终止 NUL 字符(可能还有一些剩余的空格)在字符串的开头结束,从而使输出显示为空。

编辑:看到编辑后的问题包含reverse的代码,除了上面的问题,你的reverse把终止的NUL作为反转字符串的第一个字符,这导致它是空字符串(在某些情况下,您的第二个 reverse 将其放回原处,因此如果不打印第一个 reverse 的输出,您将看不到它)。注意input[len] 包含'\0',而不是字符串本身的最后一个字符。

edit 2:此外,您实际上并没有在使用 block 之前终止该字符串。可能是未初始化的数组经常碰巧包含用于终止字符串的零,但是为了使程序正确,您绝对需要在输入循环之后立即使用block[i] = '\0'; 终止它。同样,您需要确保reversetrim 的输出的NUL 终止(在trim 的情况下,在我看来,这已经作为循环条件i &lt;= len 而不是@987654335 @,但这并不是很难分辨的好代码的标志)。

【讨论】:

  • 我已将trim 更改为返回等于output 长度的int,并且我使用该数字作为reverse 第二个实例的参数。现在,它根本没有返回任何东西,但我感觉离弄清楚这一点更近了。
  • @JazzBullets 我强烈建议先用一个较小的程序进行测试,并在每一步之间打印你的结果,而不是在它经历了两个reverse 和一个trim 之后。据我所知,如果我运行问题中的代码,trim 工作得很好。
猜你喜欢
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-05
  • 1970-01-01
  • 2012-11-12
相关资源
最近更新 更多