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;
}
,依此类推。我不明白我在做什么错?
答案 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 );
在scanf
将MAX_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
循环将永远循环。