C GOTO命令创建无限循环

时间:2018-08-21 14:52:25

标签: c goto

async

如果用户输入的字符串长度不等于10,那么我的预期输出应该是:打印出“输入字符串或长度10”,并等待用户输入另一个字符串,然后检查其长度。

我删除了Goto语句,并检查了长度不等于10的字符串,它会计算并输出长度,如下所示:

defer

我在其他地方的其他代码中也执行过类似的Goto命令执行操作,但是对于此操作:如果我尝试输入长度不等于10的字符串,它会显示该长度,然后进入无限循环或打印类似这个:

#include <stdio.h>

int main(void)
{
    char str[10];int i,length=0;
    line:
    { 
        printf("Enter string of length 10 \n");
        scanf("%[A-Za-z]s", str);
    }
    for (i=0; str[i] != '\0'; i++)
    {
        length++;
    }
    if (length != 10)
    {
        printf("Length of string is : %d \n Give a string of length 10 please \n", length);
        goto line;
    }
    else 
    {
        printf("length = %d \n", length);
        printf("Your String is: %s", str);
    }
    return 0;
}

,依此类推。我不明白我在做什么错?

3 个答案:

答案 0 :(得分:5)

您没有适当限制用户可以输入多少个字符。

数组str可以容纳10个字节,但是对scanf的调用并不限制用户输入更多字符。结果,如果输入了太多字符,则有可能在数组末尾进行写操作。这会调用undefined behavior,在这种情况下会导致代码崩溃。

您需要将格式说明符更改为scanf,以最多允许9个字符(因为您需要为空终止符保存1个字节):

scanf("%9[A-Za-z]", str);

然后您就可以摆脱长度检查(以及相关的goto),因为不再可以输入10或更长的字符串。

如果您希望用户最多可以输入10个字符,则需要将str的大小增加到11。

答案 1 :(得分:2)

要容纳10个字符的字符串,str必须至少 个11个元素宽:

char str[11]; // holds up to 10 characters plus 0 terminator

不幸的是,%s说明符不知道目标缓冲区有多大-如果输入的字符数超出了目标缓冲区可容纳的大小,则这些多余的字符将被写到缓冲区末尾,可能破坏重要数据。如果要将输入流中读取的字符数限制为str,则可以在格式中添加字段宽度:

scanf( "%10s", str );

不幸的是,与printf不同,您不能将字段指定为参数-必须将其硬编码为格式字符串。使用宏可以解决此问题:

#define MAX_INPUT_LENGTH 10 // or whatever value
#define STR(x) #x           // "stringify" the argument           
#define EXP(x) STR(x)       // expand and then stringify the argument

#define FMT STR(%) EXP(MAX_INPUT_LENGTH) STR(s) // will expand to "%" "10" "s"

所以你可以写

char str[MAX_INPUT_LENGTH + 1]; // +1 for terminator
scanf( FMT, str );

,不用担心在str末尾写。

现在,关于goto ...

有一种很多更好的方法可以完全不使用goto,并且更容易理解:

#include <string.h> // for strlen
...
char str[MAX_INPUT_LENGTH + 1];
size_t len = 0;
do
{
  printf( "Enter a string that's 10 characters long: " );
  scanf( FMT, str );
} while ( strlen( str ) != 10 );

现在,按照书面规定,这有一些问题。首先,如果您输入的字符多于MAX_INPUT_LENGTH,则未读字符将留在输入流中,以准备下次读入。您可能需要先清除所有未读的字符,然后再要求更多:

do
{
  printf( "Enter a string that's 10 characters long: " );
  scanf( FMT, str );
  while ( getchar() != '\n' ) // consume unread characters up to the newline
    ; // empty loop 
} while ( strlen( str ) != 10 );

scanfMAX_INPUT_LENGTH个字符读入str之后,它会清除所有多余的字符,直到换行符为止。

但是还有一个问题-如果字符串长度不正确,您将不会收到友好的错误消息-您将再次得到原始提示。如果要编写错误消息,则需要在do循环的正文中进行另一项检查:

do
{
  printf( "Enter a string that's 10 characters long: " );
  scanf( FMT, str );
  while ( getchar() != '\n' )
    ; // empty loop
  if ( strlen( str ) != 10 )
    printf( "Input is not the right length, try again.\n" );
} while ( strlen( str ) != 10 );

两次调用strlen有点难看,因此我们可以保存第一次strlen调用的结果以供再次使用。要将其用作循环控制表达式的一部分,必须在循环主体之外声明它:

size_t len = 0;
do
{
  printf( "Enter a string that's 10 characters long: " );
  scanf( FMT, str );
  while ( getchar() != '\n' )
    ; // empty loop
  if ( (len = strlen( str )) != 10 ) // assign as part of the check
    printf( "Input is not the right length, try again.\n" );
} while ( len != 10 );

修改

应该提到的,您可以使用fgets而不是scanf来避免缓冲区溢出问题和宏欺骗:

size_t len = 0;
do
{
  printf( "Enter a string that's 10 characters long: " );
  if ( fgets( str, sizeof str, stdin ) )
  {
    while ( getchar() != '\n' )
      ; // empty loop
    if ( (len = strlen( str )) != 10 ) // assign as part of the check
      printf( "Input is not the right length, try again.\n" );
  }
  else
  {
    // EOF or error on input, handle as appropriate
  }
} while ( len != 10 );

要检查str是否有非字母输入,请使用strpbrk函数:

#define NON_ALPHA "0123456789!@#$%^&*()-_=+[]{};:,.<>/?'\\|\"";
...
size_t len = 0;
char *non_alpha_ptr = NULL;

do
{
  printf( "Enter a string that's 10 characters long: " );
  if ( fgets( str, sizeof str, stdin ) )
  {
    while ( getchar() != '\n' )
      ; // empty loop
    if ( (len = strlen( str )) != 10 ) // assign as part of the check
      printf( "Input is not the right length, try again.\n" );
    else if ( (non_alpha_ptr = strpbrk( str, NON_ALPHA ) ) )
      printf( "Input is not strictly alphabetic, try again.\n" );
  }
  else
  {
    // EOF or error on input, handle as appropriate
  }
} while ( len != 10 || non_alpha_ptr != NULL );

答案 2 :(得分:0)

程序最重要的问题是您正在分配内存用于 {{1}中有10个char(10个字节),但是str正在存储11个scanf(11个字节)(其中10个char是输入+插入的1个前哨/分隔符char char

因此,以下行的结果-

'\0'
即使您仅输入10个if (length != 10)

也可能是第一次,并且第一次输入 true 时,回头看,char只会增加,因为length循环中没有length会减少。因此goto循环将永远循环。