Turtle Graphics 程序上的不可读输入 (C)
Unreadable input on Turtle Graphics program ( C )
我被要求做一个简单的 20x20 海龟图形程序,但出于某种原因,我遇到了一个问题,该问题可能与第 42 行到第 150 行有关(只有它们最初在 post 但我编辑了它因为有人让我发表评论):
#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,而不是步骤),但由于某种原因 'w'
而未读取 'w'
任何其他输入,如 'p'、'e' 甚至 'l' 和 'r' 都可以正常工作,但 'w' 特别无效,如果我使用 'p'('e' 不算数,因为它会停止重复)然后 'w' 输入也会被识别
所有这些 ifs 的算法有点糟糕,但它是我自己能想到的最简单、最容易解释的算法
#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;
}
当我运行你的程序并输入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
语句(假设 compass
是 int
而不是字符串):
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 );
}
之所以效率更高,是因为计算机更擅长处理数字而不是字符串。
我被要求做一个简单的 20x20 海龟图形程序,但出于某种原因,我遇到了一个问题,该问题可能与第 42 行到第 150 行有关(只有它们最初在 post 但我编辑了它因为有人让我发表评论):
#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,而不是步骤),但由于某种原因 'w'
而未读取 'w'任何其他输入,如 'p'、'e' 甚至 'l' 和 'r' 都可以正常工作,但 'w' 特别无效,如果我使用 'p'('e' 不算数,因为它会停止重复)然后 'w' 输入也会被识别
所有这些 ifs 的算法有点糟糕,但它是我自己能想到的最简单、最容易解释的算法
#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;
}
当我运行你的程序并输入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
语句(假设 compass
是 int
而不是字符串):
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 );
}
之所以效率更高,是因为计算机更擅长处理数字而不是字符串。