【问题标题】:C: Printing from char array produces erroneous charactersC:从 char 数组打印会产生错误字符
【发布时间】:2020-12-17 11:47:57
【问题描述】:

K. N. King 的 C 编程解决方案:一种现代方法,第 2 版,第 8 章,编程项目 14,会产生正确和不正确的不同输出。示例如下:

Reversal of sentence: you can't swallow a cage can you?
Reversal of sentence: you can't swallow a cage can you�(�?
Reversal of sentence: you can't swallow a cage can you��x�?
Reversal of sentence: you can't swallow a cage can you�Ց�?

如示例输入所示,正确的输出应该是:

Enter a sentence: you can cage a swallow can't you?
Reversal of sentence: you can't swallow a cage can you?

我自己的解决方案和下面的解决方案(由 Github 用户 @williamgherman 提供;为便于阅读稍作修改)都产生不同的输出。

#include <stdio.h>

int main(void)
{
    char ch, terminator, sentence[100] = {0};
    int i = 0, j;

    printf("Enter a sentence: ");
    for (i = 0; (ch = getchar()) != '\n' && i < 100; i++) {
        if (ch == '.' || ch == '!' || ch == '?') {
            terminator = ch;
            break;
        }
        sentence[i] = ch;
    }

    printf("Reversal of sentence: ");
    while (i >= 0) {
        while (sentence[--i] != ' ' && i != 0)
            ;
        j = i == 0 ? 0 : i + 1;
        while (sentence[j] != ' ' && sentence[j] != '\0')
            putchar(sentence[j++]);
        if (i > 0)
            putchar(' ');
    }

    printf("%c\n", terminator);

    return 0;
}

尽管仔细检查了代码,并通过纸上的示例输入运行,我还是找不到答案。

代码为什么会产生这些不同的输出,正确的和不正确的?是什么产生了错误字符?

【问题讨论】:

  • while (i &gt;= 0) 循环内,i 在某些时候变为负数。您需要对此进行调试。这是开始学习如何使用调试器的绝佳机会。
  • while 循环的最后一次迭代有i == 0。所以sentence[--i] 访问数组外的sentence[-1]
  • @Jabberwocky -- 你能推荐一个调试器吗?我正在使用 GCC 编译我的代码。
  • 使用 gdb。如果您使用一些 IDE(我强烈推荐),很可能会包含调试器。

标签: c loops char reverse c-strings


【解决方案1】:

很可能,问题在于用于反向打印句子的while 循环的退出条件

    while (i >= 0) {
        while (sentence[--i] != ' ' && i != 0) ;
    ....

考虑代码要打印第一个单词 (i=3) 的情况:

  • 第二个 while 将 i 一直递减到 0
  • 然后代码将打印单词“you”(位置 0 到 2,包括)
  • 此时 i=0,第一个 while 仍然为真
  • 第二个 while 将 i 递减到 -1,并将继续递减到 -2、-3,直到找到一个空格。
  • 代码将打印索引为 -1、-2、-3 的单词,并根据这些未定义的值打印一个字符串。

【讨论】:

  • 谢谢!将外部while循环更改为while (i &gt; 0),或将内部while循环更改为while (sentence[--i] != ' ' &amp;&amp; i &gt;= 0),即可解决问题。
【解决方案2】:

是的,如上面的答案所述,问题在此块内

while (i >= 0) {
        while (sentence[--i] != ' ' && i != 0) ;
    ....

你应该把i != 0部分改成i &gt;= 0,这应该可以解决问题

问题是 i 递减 sentence[--i] 发生在 之前 i != 0 ,所以想象一下从 i = 0 开始的 while 循环 - 这将意味着 sentence[i - 1] 将(可能)不是 @ 987654327@ 和 i 不会等于零。

【讨论】:

    【解决方案3】:

    对于初学者,程序具有未定义的行为,因为变量terminator 未初始化。用户可以在不提供这些字符之一的情况下按 Enter 键".!?"

    for (i = 0; (ch = getchar()) != '\n' && i < 100; i++) {
                                 ^^^^^^^                       
    

    在这种情况下这个语句

    printf("%c\n", terminator);
    

    将输出一个不确定的值。

    此外,用户可以按相应的组合键中断循环,但循环不会处理这种情况。

    如果用户在循环的最开始按下 Enter 键,那么 i 将等于 0 在这种情况下是内部 while 循环

    while (i >= 0) {
        while (sentence[--i] != ' ' && i != 0)
                        ^^^
            ;
    

    将调用未定义的行为。

    此外,在终止字符 (".!?") 之前,句子可以包含空格。因此这个循环

        while (sentence[--i] != ' ' && i != 0)
            ;
        j = i == 0 ? 0 : i + 1;
    

    再次将被错误终止。也就是说,j 将等于 i + 1,其中存储了一个零字符(前提是用户没有输入数组语句的所有 100 个元素)。

    除此之外,程序不会输出单词之间存在的空格数。它只尝试输出一个空格

    putchar(' ');
    

    考虑到用户可以输入制表符'\t',而不是空格字符' '

    下面显示了如何编写程序。

    #include <stdio.h>
    #include <ctype.h>
    #include <string.h>
    
    int main(void) 
    {
        const char *punctuation = ".?!";
    
        enum { N = 100 };
        char s[N] = "";
        char terminator = '\0';
        
        printf( "Enter a sentence: " );
        
        size_t n = 0;
    
        for ( int c; n < N && ( c = getchar() ) != EOF && c != '\n'; n++ )
        {
            if ( strchr( punctuation, c ) )
            {
                terminator = c;
                break;
            }
            
            s[n] = c;
        }
        
        printf( "Reversal of sentence: " );
    
        putchar( '\"' );
        
        size_t i = 0;
        
        while ( i != n && isblank( ( unsigned char )s[i] ) ) putchar( s[i++] );
        
        for ( size_t j = n; i != n; )
        {
            while ( i != n && !isblank( ( unsigned char )s[i] ) ) i++;
            
            while ( isblank( ( unsigned char )s[j-1] ) ) j--;
    
            size_t k = j;
            while ( j != 0 && !isblank( ( unsigned char )s[j-1] ) ) j--;
                    
            for ( size_t l = j; l != k; l++ ) putchar( s[l] );
    
            while ( i != n && isblank( ( unsigned char )s[i] ) ) putchar( s[i++] );
        }
        
        if ( terminator ) putchar( terminator );
        putchar( '\"' );
        
        return 0;
    }
    

    程序输出可能看起来像

    Enter a sentence: you can  cage   a    swallow     can't      you?
    Reversal of sentence: "you can't  swallow   a    cage     can      you?"
    

    如您所见,程序保留了输入句子中包含的所有空格。

    【讨论】:

    • 尽管您强调了重要的改进,但它们超出了 C Programming: A Modern Approach, 2nd Edition 中提出的 Programming Project 14 的范围。例如,假设所有输入都以 .!?. 结尾
    • @s164151 有什么问题?所示程序执行所需的操作。但除此之外,与所讨论的程序相比,它是一个正确的程序,即使在其他答案中也有备注。
    • @s164151 例如,尝试使用在一个终止字符之前包含两个或三个空格的句子的更新程序,看看你会得到什么。或者尝试使用制表符代替空格。
    猜你喜欢
    • 1970-01-01
    • 2021-03-14
    • 1970-01-01
    • 2014-04-08
    • 2012-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多