我有这段代码:
char* receiveInput(){
char *s;
scanf("%s",s);
return s;
}
int main()
{
char *str = receiveInput();
int length = strlen(str);
printf("Your string is %s, length is %d\n", str, length);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我收到这个输出:
Your string is hellàÿ", length is 11
Run Code Online (Sandbox Code Playgroud)
我的意见是:
helloworld!
Run Code Online (Sandbox Code Playgroud)
有人可以解释为什么,以及为什么这种编码风格很糟糕,提前谢谢
Chr*_*utz 20
几个问题已经解决了你做错了什么以及如何解决它,但你也说过(强调我的):
有人可以解释为什么,以及为什么这种编码风格很糟糕
我认为scanf阅读输入是一种可怕的方式.它与之不一致printf,很容易忘记检查错误,难以从错误中恢复,并且与普通(并且更容易正确)读取操作(如fgets公司)无关.
首先,请注意"%s"格式只有在看到空格时才会读取.为什么是空白?为什么"%s"打印出整个字符串,但是读取字符串的容量有限?
如果你想在一整行读取,你可能经常会习惯这样做,scanf提供...用"%[^\n]".什么?那是什么?什么时候成为Perl?
但真正的问题是这些都不安全.它们都自由溢出,没有边界检查.想要检查边界?好吧,你明白了"%10s"(并且"%10[^\n]"开始变得更糟).这将只读取9个字符,并自动添加终止空字符.所以这很好......因为我们的数组大小永远不需要改变.
如果我们想将数组的大小作为参数传递给scanf?printf可以做到这一点:
char string[] = "Hello, world!";
printf("%.*s\n", sizeof string, string); // prints whole message;
printf("%.*s\n", 6, string); // prints just "Hello,"
Run Code Online (Sandbox Code Playgroud)
想做同样的事情scanf吗?这是如何做:
static char tmp[/*bit twiddling to get the log10 of SIZE_MAX plus a few*/];
// if we did the math right we shouldn't need to use snprintf
snprintf(tmp, sizeof tmp, "%%%us", bufsize);
scanf(tmp, buffer);
Run Code Online (Sandbox Code Playgroud)
这是正确的 - scanf不支持"%.*s"变量精度printf,所以要做动态边界检查scanf我们必须在临时缓冲区中构造我们自己的格式字符串.这是各种各样的坏事,即使它在这里实际上是安全的,对任何人来说都是一个非常糟糕的主意.
与此同时,让我们看看另一个世界.让我们来看看世界fgets.以下是我们如何阅读以下数据fgets:
fgets(buffer, bufsize, stdin);
Run Code Online (Sandbox Code Playgroud)
无限的头痛,没有浪费的处理器时间将整数精度转换为一个字符串,只能将库重新分解为一个整数,并且所有相关元素都坐在一行上,以便我们看看它们如何协同工作.
当然,这可能不会读取整行.如果行短于bufsize - 1字符,它将只读取整行.以下是我们如何阅读整行:
char *readline(FILE *file)
{
size_t size = 80; // start off small
size_t curr = 0;
char *buffer = malloc(size);
while(fgets(buffer + curr, size - curr, file))
{
if(strchr(buffer + curr, '\n')) return buffer; // success
curr = size - 1;
size *= 2;
char *tmp = realloc(buffer, size);
if(tmp == NULL) /* handle error */;
buffer = tmp;
}
/* handle error */;
}
Run Code Online (Sandbox Code Playgroud)
该curr变量是阻止我们重新检查我们已经读出的数据进行优化,并且是不必要的(虽然有用,因为我们读更多的数据).如果您愿意,我们甚至可以使用返回值strchr去除结束"\n"字符.
另请注意,size_t size = 80;起始位置完全是任意的.我们可以使用81,或79或100,或将其作为用户提供的参数添加到函数中.我们甚至可以添加一个int (*inc)(int)参数,然后更改size *= 2;为size = inc(size);允许用户控制数组增长的速度.当重新分配成本高昂且需要读取和处理大量数据时,这些对于提高效率非常有用.
我们可以写相同的scanf,但想想我们必须重写格式字符串的次数.我们可以将它限制为一个恒定的增量,而不是上面实现的加倍(容易),并且永远不必调整格式字符串; 我们可以放弃并只存储数字,按上述方法进行数学处理,并snprintf在每次重新分配时将其转换为格式字符串,以便scanf将其转换回相同的数字; 我们可以限制我们的增长和起始位置,以便我们可以手动调整格式字符串(例如,只是增加数字),但这可能会在一段时间后变得毛茸茸并且可能需要递归(!)才能干净地工作.
此外,很难将阅读scanf与阅读与其他功能混合在一起.为什么?假设您要从一行读取整数,然后从下一行读取一个字符串.你试试这个:
int i;
char buf[BUSIZE];
scanf("%i", &i);
fgets(buf, BUFSIZE, stdin);
Run Code Online (Sandbox Code Playgroud)
这将读取"2",但然后fgets将读取一个空行,因为scanf没有读取换行符!好的,拿两个:
...
scanf("%i\n", &i);
...
Run Code Online (Sandbox Code Playgroud)
你认为这会占用换行符,而且它确实会 - 但它也会占用下一行的空白空格,因为scanf无法区分换行符和其他形式的空格.(另外,事实证明你正在编写一个Python解析器,并且在行中引入空格很重要.)为了使这个工作,你必须getchar在换行符中调用或读取一些内容并将其丢弃:
...
scanf("%i", &i);
getchar();
...
Run Code Online (Sandbox Code Playgroud)
这不是很傻吗?如果你scanf在一个函数中使用会发生什么,但是不要调用,getchar因为你不知道下一个读取scanf是否会变得更健全(或者下一个字符是否会成为换行符)?突然间,处理这种情况的最佳方法似乎是选择其中一种方式:我们是否scanf专门使用并且永远不能访问fgets样式的完全控制输入,或者我们是否fgets专门使用并且难以执行复杂的解析?
实际上,答案是我们没有.我们专门使用fgets(或非scanf函数),当我们需要scanf类似功能时,我们只需要调用sscanf字符串!我们不需要scanf不必要地破坏我们的文件流!我们可以对我们想要的输入进行所有精确控制,并且仍然可以获得scanf格式化的所有功能.即使我们不能,许多scanf格式选项在标准库附近直接对应的功能,如无限更灵活strtol和strtod功能(和朋友).另外,i = strtoumax(str, NULL)对于C99大小的整数类型看起来比看起来更清晰scanf("%" SCNuMAX, &i);,并且更安全(我们可以strtoumax对较小的类型使用该行不变,让隐式转换处理额外的位,但是scanf我们必须暂时uintmax_t读取) .
这个故事的寓意:避免scanf.如果你需要它提供的格式,并且不想(或不能)自己(更有效地)自己做,请使用fgets/ sscanf.
peo*_*oro 11
scanf 没有为你分配内存.
您需要为传递给的变量分配内存scanf.
你可以这样做:
char* receiveInput(){
char *s = (char*) malloc( 100 );
scanf("%s",s);
return s;
}
Run Code Online (Sandbox Code Playgroud)
但警告:
调用的函数receiveInput将获取返回的内存的所有权:free(str)打印后你必须这样做main.(以这种方式取消所有权通常不被视为一种好的做法).
一个简单的解决方法是将分配的内存作为参数.
如果输入字符串长于99(在我的情况下)你的程序将遭受缓冲区溢出(这是它已经发生的事情).
一个简单的解决方法是传递scanf缓冲区的长度:
scanf("%99s",s);
Run Code Online (Sandbox Code Playgroud)固定代码可能是这样的:
// s must be of at least 100 chars!!!
char* receiveInput( char *s ){
scanf("%99s",s);
return s;
}
int main()
{
char str[100];
receiveInput( str );
int length = strlen(str);
printf("Your string is %s, length is %d\n", str, length);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
74386 次 |
| 最近记录: |