【问题标题】:Unreadable input on Turtle Graphics program ( C )Turtle Graphics 程序 (C) 上的不可读输入
【发布时间】:2021-10-06 07:10:53
【问题描述】:

我被要求做一个简单的 20x20 海龟图形程序,但由于某种原因,我遇到了一个可能与第 42 行到第 150 行相关的问题(只有他们最初在帖子上,但我编辑了它,因为有人让我这样做cmets):

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>
int floor[20][20], step, p1 = 0, p2 = 0;
char compass[6] = "westm", com[1], pen[3] = "up";
int main()
{
    com[0] = 'x';
    for (int i = 0; i < 20; i++)
    {
        for (int j = 0; j < 20; j++)
        {
            floor[i][j] = 0;
        }

    }
    while (com[0] != 'e' && com[0] != 'E')
    {
        com[0] = 'x';
        printf("Enter a command \n");
        scanf("%c", &com[0]);
        getchar();
        if (com[0] == 'e' || com[0] == 'E')
        {
            printf("End \n");
        }
        else
        {
            if (com[0] == 'u' || com[0] == 'U')
            {
                strncpy(pen, "up", 3);
                printf("The pen was turned up \n");
            }
            if (com[0] == 'd' || com[0] == 'D')
            {
                strncpy(pen, "do", 3);
                floor[p1][p2] = 1;
                printf("The pen was turned down \n");
            }
            if (com[0] == 'r' || com[0] == 'R')
            {
                if (!strcmp(compass, "westm"))
                {
                    strncpy(compass, "south", 6);
                }
                if (!strcmp(compass, "south"))
                {
                    strncpy(compass, "eastm", 6);
                }
                if (!strcmp(compass, "eastm"))
                {
                    strncpy(compass, "north", 6);
                }
                if (!strcmp(compass, "north"))
                {
                    strncpy(compass, "westm", 6);
                }
                printf("The turtle turned right \n");
            }
            if (com[0] == 'l' || com[0] == 'L')
            {
                if (!strcmp(compass, "westm"))
                {
                    strncpy(compass, "north", 6);
                }
                if (!strcmp(compass, "south"))
                {
                    strncpy(compass, "westm", 6);
                }
                if (!strcmp(compass, "eastm"))
                {
                    strncpy(compass, "south", 6);
                }
                if (!strcmp(compass, "north"))
                {
                    strncpy(compass, "eastm", 6);
                }
                printf("The turtle turned left \n");
            }
            if (com[0] == 'w' || com[0] == 'W')
            {
                step = 2147483647;
                if (!strcmp(compass, "westm"))
                {
                    while (step + p2 > 19)
                    {
                        printf("Type a valid number of steps \n");
                        scanf("%d", &step);
                        getchar();
                    }
                    if (!strcmp(pen, "do"))
                    {
                        for (int i = 0; i <= p2 + step; i++)
                        {
                            floor[p1][p2 + i] = 1;
                        }
                    }
                    p2 = floor + p2;
                }
                if (!strcmp(compass, "north"))
                {
                    while (p1 - step < 0)
                    {
                        scanf("%d", &step);
                        getchar();
                    }
                    if (!strcmp(pen, "do"))
                    {
                        for (int i = 0; i <= p1 - step; i++)
                        {
                            floor[p1 - i][p2] = 1;
                        }
                    }
                    p1 = p1 - step;
                }
                if (!strcmp(compass, "eastm"))
                {
                    while (p2 - step < 0)
                    {
                        scanf("%d", &step);
                        getchar();
                    }
                    if (!strcmp(pen, "do"))
                    {
                        for (int i = 0; i <= p2 - step; i++)
                        {
                            floor[p1][p2 - i] = 1;
                        }
                    }
                    p2 = p2 - step;
                }
                if (!strcmp(compass, "south"))
                {
                    while (step + p2 > 19)
                    {
                        scanf("%d", &step);
                        getchar();
                    }
                    if (!strcmp(pen, "do"))
                    {
                        for (int i = 0; i <= p1 + step; i++)
                        {
                            floor[p1 + i][p2] = 1;
                        }
                    }
                    p1 = p1 + step;
                }
            }
            if (com[0] == 'p' || com[0] == 'P')
            {
                for (int i = 0; i < 20; i++)
                {
                    for (int j = 0; j < 20; j++)
                    {
                        if (floor[i][j] == 0)
                        {
                            printf(". ");
                        }
                        else
                        {
                            printf("* ");
                        }
                    }
                    printf("\n");
                }
            }
        }
    }
}

基本上,在代码的开头有一个“while”等待用户输入,直到'e'和'E'都不同,它似乎在大多数情况下都可以正常工作

问题在于,当我在右转或左转后行走(通过在字符“r”或“l”之后输入“w”)或尝试多次使用“w”输入时,程序会不断要求输入(对于变量 com,而不是 step)由于某种原因没有读取 'w'

任何其他输入,如“p”、“e”甚至“l”和“r”都可以正常工作,但“w”特别不起作用,如果我使用“p”(“e”不'不计数,因为它会停止重复)然后'w'输入也将被识别

所有这些 if 的算法有点糟糕,但这是我自己能想到的最简单和最容易解释的方法

【问题讨论】:

  • 可能不相关,但使用字符串进行状态跟踪、比较和覆盖它们极其效率低下且难以编写。
  • 更不用说使用一系列独立的if 语句用于互斥条件集,而不是普通的if / else if 链。
  • 什么是step,为什么初始化为INT_MAX?什么是p1p2Signed integer overflow 是未定义的行为,但真的不确定 while (step + p2 &gt; 19) 是否会发生这种情况。看起来很危险。
  • @yano step是一个int,表示乌龟要走多少帧,p1和p2是int,表示乌龟在矩阵floor[20][20]上的当前位置,p1是该行和 p2 是我在 while 开始时将 step 设置为可能的最高值的列,以避免用户输入的输入可能会使 p1 或 p2 高于 19,同时输入任何一个'w' 时间
  • @andreas-wenzel ummmm 我应该用整个代码编辑原始帖子,还是可以在我的问题或评论上放置一个 pastebin 链接?我是 stackoverflow 的新手,所以我真的不知道是否允许这样做

标签: c input graphics output repeat


【解决方案1】:

当我运行你的程序并输入l作为第一个命令并输入w作为第二个命令时,它将执行以下循环:

while (p2 - step < 0)
{
    scanf("%d", &step);
    getchar();
}

因为此时p2 == 0,此循环将永远运行,直到用户输入零或负数。这似乎不是故意的。此外,程序在不事先告诉用户输入什么输入的情况下读取用户的输入似乎也不是有意的。

诊断此类问题的最佳方法是在debugger 中逐行运行程序,同时监控所有变量的值。这样,很容易看出您的程序在什么时候停止按预期运行。

另外,你的程序也存在以下问题:


似乎getchar(); 语句应该丢弃换行符。但是,这仅在用户的输入采用预期格式时才有效,即换行符位于预期位置时。如果用户没有输入任何字符或输入太多字符,那么这并不总是有效。

对于基于行的输入,我建议你使用函数fgets而不是scanf/getchar,因为函数fgets总是一次只读取一行(假设缓冲区很大足以存储整条线)。您可能想阅读以下内容:A beginners' guide away from scanf()


一般来说,声明一个只有一个成员的数组是没有意义的。因此,将com 声明为简单的char 而不是char 的数组可能更有意义。


下面的代码有点繁琐:

if (com[0] == 'e' || com[0] == 'E')
{
    [...]
}
else
{
    if (com[0] == 'u' || com[0] == 'U')
    {
        [...]
    }
    if (com[0] == 'd' || com[0] == 'D')
    {
        [...]
    }
    if (com[0] == 'r' || com[0] == 'R')
    {
        [...]
    }
    if (com[0] == 'l' || com[0] == 'L')
    {
        [...]
    }
    if (com[0] == 'w' || com[0] == 'W')
    {
        [...]
    }
    if (com[0] == 'p' || com[0] == 'P')
    {
        [...]
    }
}

可以简化为:

switch ( toupper( (unsigned char)com[0] ) )
{
    case 'E':
        [...]
        break;
    case 'U':
        [...]
        break;
    case 'D':
        [...]
        break;
    case 'R':
        [...]
        break;
    case 'L':
        [...]
        break;
    case 'W':
        [...]
        break;
    case 'P':
        [...]
        break;
    default:
        fprintf( stderr, "unexpected error!\n" );
        exit( EXIT_FAILURE );
}

以下代码错误:

if (!strcmp(compass, "westm"))
{
    strncpy(compass, "south", 6);
}
if (!strcmp(compass, "south"))
{
    strncpy(compass, "eastm", 6);
}
if (!strcmp(compass, "eastm"))
{
    strncpy(compass, "north", 6);
}
if (!strcmp(compass, "north"))
{
    strncpy(compass, "westm", 6);
}

例如,如果compass 包含字符串"westm",那么第一个if 块会将字符串更改为"south"。但现在第二个if 块的条件为真,所以第二个if 块将其更改为"eastm"。现在第三个if 块的条件为真,所以第三个if 块将其更改为"north"。现在第四个if 块的条件为真,所以第四个if 块将它改回"westm"。这样,您将完成一个完整的旋转。这可能不是你想要的。

为了打破这个链条,你应该在每个if语句之前添加一个else,除了第一个if语句:

if (!strcmp(compass, "westm"))
{
    strncpy(compass, "south", 6);
}
else if (!strcmp(compass, "south"))
{
    strncpy(compass, "eastm", 6);
}
else if (!strcmp(compass, "eastm"))
{
    strncpy(compass, "north", 6);
}
else if (!strcmp(compass, "north"))
{
    strncpy(compass, "westm", 6);
}

但是,与其将方向 (compass) 存储为字符串,不如将其存储为 enum 更有效,如下所示:

enum direction
{
    DIRECTION_NORTH,
    DIRECTION_SOUTH,
    DIRECTION_WEST,
    DIRECTION_EAST
};

这样,您可以简单地编写以下语句,而不是编写 if/else if 语句链(假设 compassint 而不是字符串):

switch ( compass )
{
    case DIRECTION_WEST:
        compass = DIRECTION_SOUTH;
        break;
    case DIRECTION_SOUTH:
        compass = DIRECTION_EAST;
        break;
    case DIRECTION_EAST:
        compass = DIRECTION_NORTH;
        break;
    case DIRECTION_NORTH:
        compass = DIRECTION_WEST;
        break;
    default:
        fprintf( stderr, "unexpected error!\n" );
        exit( EXIT_FAILURE );
}

这样做更有效的原因是计算机比字符串更擅长处理数字。

【讨论】:

    【解决方案2】:
    #include "turtle.h"
    
    int main()
    {
        turtle_init(300, 300);          // initialize the image to be 600x600
    
        turtle_forward(50);
        turtle_turn_left(90);
        turtle_forward(50);
        turtle_draw_turtle();
    
        turtle_save_bmp("output.bmp");  // save the turtle drawing
    
        return EXIT_SUCCESS;
    }
    

    【讨论】:

    • 不过,我不需要将任何东西保存为图像,应该在控制台上看到使用 .'s 和 *'s 制作的绘图,这也是我被要求使其工作的方式就像现在的代码一样,用户必须在控制台上输入 w、l、rp、e、u 或 d 来让“乌龟”行动
    猜你喜欢
    • 2016-07-04
    • 1970-01-01
    • 2019-01-04
    • 2012-10-31
    • 1970-01-01
    • 1970-01-01
    • 2018-06-14
    • 1970-01-01
    • 2020-09-26
    相关资源
    最近更新 更多