我的代码问题:
这段代码只让我输入一个命令,然后跳出循环,甚至不需要输入“退出”
--
您的代码中存在大量错误。识别代码中的问题的最大障碍之一(虽然不是错误)可能是代码中缺少空格。将所有内容混在一起会使您的代码非常难以阅读(尤其是对于老眼睛来说)。将代码上的间距打开一点。
正如上面评论中提到的,您的第一个显示停止问题是在未初始化时使用
str1
。这会调用未定义的行为,然后代码的定义操作此时就结束了。您的代码可能出现段错误、看起来正常工作或介于两者之间。
当您进行用户输入时,建议您使用面向行的输入函数,例如
fgets()
或POSIXgetline()
。这一更改避免了与尝试使用像 scanf()
这样的 formatted-input函数获取用户输入相关的大量陷阱。如果您不知道每个陷阱与其使用相关联 - 不要将其用于用户输入。本网站上 10 个用户输入问题中有 9 个与
scanf()
的误用有关。
此外,不要吝惜缓冲区大小!!。如果用户输入
"iskabibble"
而不是 "quit"
会发生什么?不适合str1[5]
的字符如何处理?如果猫踩在键盘上输入了 100 个字符怎么办?太长 10,000 个字符比太短一个字符要好。使用 fgets()
和足够大小的缓冲区获取输入,然后使用 sscanf()
解析所需的值,而不是尝试使用 scanf()
两者都执行,例如
#define MAXC 512 /* if you need a constant, #define one (or more)
* ( don't skimp on buffer size!! )
*/
int main (void) {
char buf[MAXC]; /* buffer to store all user input */
int n; /* if possible, declare variables at beginning of scope */
fputs ("please input n for (n x n array): ", stdout);
if (!fgets (buf, MAXC, stdin)) { /* read all user input with fgets() */
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &n) != 1) { /* validate every conversion */
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
重用单个缓冲区来处理用户输入可以大大简化事情。
虽然可以使用 VLA(可变长度数组)来解决练习问题,但请注意,从 C11 开始,编译器不需要支持它们,并且在 C99 之前也不支持它们。最好为生产代码动态分配。
您的变量声明应该在需要变量的范围内。这有助于防止常见变量的变量遮蔽,例如
i
、j
等。代码中的 300 行以下。例如,只要循环外部不需要循环变量,就可以将循环变量声明为循环声明的一部分,例如
int arr[n][n]; /* VLA's are an 'optional' feature since C11 */
for (int i = 0; i < n; i++) { /* i, j can be decalred with loop scope */
for (int j = 0; j < n; j++) {
arr[i][j] = -1;
}
arr[i][0] = i;
}
当您需要用户输入特定输入时,最好不断循环,直到用户提供有效输入(尊重他们通过使用
Ctrl + d或在 Windows 上使用 Ctrl + z 生成手动
EOF
来取消输入的能力) )。例如,需要 str1
、s
、str2
和 d
:
while (strcmp (buf, "quit")) { /* loop until quit */
char str1[MAXC] = "", str2[MAXC] = "";
int s = 0, d = 0;
while (1) { /* loop continually */
fputs ("enter str1 s str2 d: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read / validate input */
puts ("(user canceled input)");
return 0;
}
buf[strcspn (buf, "\n")] = 0; /* trim '\n' from end of buf */
if (strcmp (buf, "quit") == 0) /* if "quit", break */
break;
/* parse and validate separate values, always protect array bounds */
if (sscanf (buf, "%511s %d %511s %d", str1, &s, str2, &d) != 4) {
fputs (" error: invalid format or integer input.\n", stderr);
continue;
} /* validate range of integers (negated conditions are confusing) */
if ((0 <= s && s < n) && (0 <= d && d < n))
break; /* exit loop on good input */
else /* otherwise, handle error */
fputs (" error: value for s or d out of range.\n", stderr);
}
(注意:
fgets()
读取并包含用户按 Enter生成的
'\n'
,因此在比较 "quit"
之前,您需要使用 strcspn()
删除换行符)
由于
str1
和 str2
是使用 buf
从 sscanf()
解析而来,因此没有需要删除的 '\n'
。但是,在使用 sscanf()
时,必须使用 field-width 修饰符来保护数组边界免遭溢出 - 否则使用 scanf()
或 sscanf()
填充字符数组并不比 gets()
更安全,请参阅:为什么 gets() 如此危险,永远不应该使用!
if (strcmp (str1, "move") == 0) { /* handle move */
if (strcmp (str2, "onto") == 0) { /* onto? */
int i; /* declare i in scope needed */
// empty s
for (i = 0; i < n && arr[s][i] != -1; i++) {
arr[arr[s][i]][0] = arr[s][i];
arr[s][i] = -1;
}
// empty d
for (i = 0; i < n && arr[d][i] != -1; i++){
arr[arr[d][i]][0] = arr[d][i];
arr[d][i] = -1;
}
// now move s to d
i = 1;
while (arr[d][i] != -1) {
i++;
}
arr[d][i] = arr[s][0];
arr[s][0] = -1;
}
else if (strcmp (str2, "over") == 0) {
(void)str2; /* no-op prevents empty scope */
}
else {
continue;
}
}
else if (strcmp (str2, "pile") == 0) {
(void)str2;
}
else {
continue;
}
(注:最后的
else
不需要)
完整代码
虽然我仍然不清楚你的逻辑应该做什么,但可以按照上面所示的方式完成输入。修复逻辑就交给你了。
#include <stdio.h>
#include <string.h>
#define MAXC 512 /* if you need a constant, #define one (or more)
* ( don't skimp on buffer size!! )
*/
int main (void) {
char buf[MAXC]; /* buffer to store all user input */
int n; /* if possible, declare variables at beginning of scope */
fputs ("please input n for (n x n array): ", stdout);
if (!fgets (buf, MAXC, stdin)) { /* read all user input with fgets() */
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &n) != 1) { /* validate every conversion */
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
int arr[n][n]; /* VLA's are an 'optional' feature since C11 */
for (int i = 0; i < n; i++) { /* i, j can be decalred with loop scope */
for (int j = 0; j < n; j++) {
arr[i][j] = -1;
}
arr[i][0] = i;
}
while (strcmp (buf, "quit")) { /* loop until quit */
char str1[MAXC] = "", str2[MAXC] = "";
int s = 0, d = 0;
while (1) { /* loop continually */
fputs ("enter str1 s str2 d: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read / validate input */
puts ("(user canceled input)");
return 0;
}
buf[strcspn (buf, "\n")] = 0; /* trim '\n' from end of buf */
if (strcmp (buf, "quit") == 0) /* if "quit", break */
break;
/* parse and validate separate values, always protect array bounds */
if (sscanf (buf, "%511s %d %511s %d", str1, &s, str2, &d) != 4) {
fputs (" error: invalid format or integer input.\n", stderr);
continue;
} /* validate range of integers (negated conditions are confusing) */
if ((0 <= s && s < n) && (0 <= d && d < n))
break; /* exit loop on good input */
else /* otherwise, handle error */
fputs (" error: value for s or d out of range.\n", stderr);
}
if (strcmp (str1, "move") == 0) { /* handle move */
if (strcmp (str2, "onto") == 0) { /* onto? */
int i; /* declare i in scope needed */
// empty s
for (i = 0; i < n && arr[s][i] != -1; i++) {
arr[arr[s][i]][0] = arr[s][i];
arr[s][i] = -1;
}
// empty d
for (i = 0; i < n && arr[d][i] != -1; i++){
arr[arr[d][i]][0] = arr[d][i];
arr[d][i] = -1;
}
// now move s to d
i = 1;
while (arr[d][i] != -1) {
i++;
}
arr[d][i] = arr[s][0];
arr[s][0] = -1;
}
else if (strcmp (str2, "over") == 0) {
(void)str2; /* no-op prevents empty scope */
}
else {
continue;
}
}
else if (strcmp (str2, "pile") == 0) {
(void)str2;
}
else {
continue;
}
}
// print results
for (int i = 0; i < n; i++) {
printf ("%d:\n", i);
for (int j = 0; j < n; j++) {
if (arr[i][j] != -1)
printf (" % 3d", arr[i][j]);
else
fputs (" [ ]", stdout);
}
putchar ('\n');
}
}
示例使用/输出
故意输入错误:
$ ./bin/vla_quit
please input n for (n x n array): 5
enter str1 s str2 d: move 2 onto 3
enter str1 s str2 d: move bananas onto gorillas
error: invalid format or integer input.
enter str1 s str2 d: move 1 onto 4
enter str1 s str2 d: move -1 onto 2
error: value for s or d out of range.
enter str1 s str2 d: move 0 onto 2
enter str1 s str2 d: quit
0:
[ ] [ ] [ ] [ ] [ ]
1:
[ ] [ ] [ ] [ ] [ ]
2:
[ ] [ ] [ ] [ ] [ ]
3:
[ ] [ ] [ ] [ ] [ ]
4:
[ ] [ ] [ ] [ ] [ ]
始终使用启用警告进行编译,并且不要接受代码,直到代码在没有警告的情况下编译。要启用警告,请将
-Wall -Wextra -pedantic
添加到 gcc/clang
编译字符串(还可以考虑添加 -Wshadow
以对隐藏变量发出警告)。对于 VS(Windows 上为 cl.exe
),请使用 /W3
。所有其他编译器都会有类似的选项。阅读并理解每个警告——然后修复它。他们将识别任何问题以及问题发生的确切线路。通过聆听编译器告诉您的内容,您可以学到很多东西。
如果您还有其他问题,请告诉我。