为什么程序结束时没有“退出”?

问题描述 投票:0回答:1

我的代码问题:

这段代码只让我输入一个命令,然后跳出循环,甚至不需要输入“退出”

--

arrays c string debugging input
1个回答
1
投票

您的代码中存在大量错误。识别代码中的问题的最大障碍之一(虽然不是错误)可能是代码中缺少空格。将所有内容混在一起会使您的代码非常难以阅读(尤其是对于老眼睛来说)。将代码上的间距打开一点。

正如上面评论中提到的,您的第一个显示停止问题是在未初始化时使用

str1
。这会调用未定义的行为,然后代码的定义操作此时就结束了。您的代码可能出现段错误、看起来正常工作或介于两者之间。

当您进行用户输入时,建议您使用面向行的输入函数,例如

fgets()
或POSIX
getline()
。这一更改避免了与尝试使用像 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
。所有其他编译器都会有类似的选项。阅读并理解每个警告——然后修复它。他们将识别任何问题以及问题发生的确切线路。通过聆听编译器告诉您的内容,您可以学到很多东西。

如果您还有其他问题,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.