既然scanf() 可以帮你转换答案,为什么还要花时间转换答案?
#include <stdio.h>
int main(void)
{
float height_in_inches;
printf("Enter your height in inches: ");
if (scanf("%f", &height_in_inches) == 1)
{
float height_in_cm = height_in_inches * 2.54;
printf("You are %.2f inches or %.2f centimetres tall.\n",
height_in_inches, height_in_cm);
}
else
printf("I didn't understand what you said\n");
return(0);
}
如果你必须读取一个字符串,那么使用fgets():
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char line[4096];
printf("Enter your height in inches: ");
if (fgets(line, sizeof(line), stdin) != 0)
{
double height_in = strtod(line, 0);
double height_cm = height_in * 2.54;
printf("You are %.2f inches or %.2f centimetres tall.\n",
height_in, height_cm);
}
return(0);
}
请注意,两个程序都会在使用输入结果之前检查输入是否发生。你可以争辩说,对strtod() 的调用的错误检查是非常懒惰的。我同意。请注意,我在第一个片段中的float 和第二个片段中的double 之间切换;要么可以工作。当结果为分数时,我认为没有特别的理由将输入限制为整数值。回显输入和输出通常也是有益的;如果你得到的回应不是你认为你输入的,这是一个很好的提示,表明某些地方出了问题,并让你在代码的正确区域进行搜索。
注意:有一些小细节被掩盖在地毯下,尤其是在第一个例子中,float 和 double 与 scanf() 和 printf()。鉴于下面的评论,它们目前与 OP 无关。
对原始代码的最小修复
由于上面的代码比 OP 识别的更复杂,这里是对原始代码的一组更简单的修复。输入到数组(字符串)中的字符串是关键点;这也需要更改 scanf() 调用。
通过使用大缓冲区,我们可以假设用户无法通过在终端键入来溢出输入。但是,对于机器驱动的输入来说就不行了。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
float height_in_cm;
char height_in_inches[4096]; // Array, big enough to avoid most overflows
printf("Enter your height in inches: ");
// Missing error check on scanf() — too advanced as yet
scanf("%s", height_in_inches); // Format specifier, parentheses, ampersand
height_in_cm = atoi(height_in_inches) * 2.54;
printf("You are %s inches or %.2f centimetres tall.\n",
height_in_inches, height_in_cm);
return(0);
}
一行输入有多长?
user3629249commented:
可以通过以下方式节省大量堆栈空间:
- 注意到“int”最多只能包含 12 个字符,因此输入缓冲区的最大长度为 13 个字符(以允许 NUL 字符串终止字节)
- 将
scanf() 限制为12 个字符。 IE。 'scanf( "%12s", myCharArray );
- 在 C 中,数组的名称降级为数组的地址,因此“myCharArray”上不需要前导“&”。
第 3 点是正确的;如果您使用char myCharArray[13];,则在调用scanf() 等时不要使用&myCharArray;你只使用myCharArray。如果您滥用&,一个好的编译器会指出您的方式错误。
不过,我对第 1 点和第 2 点有疑问。 很多的麻烦可以通过注意如果用户在线输入 9876432109876543210 来避免,那么使用 scanf() 和 %12s 对消除无效输入没有太大帮助。它将在行上留下 8 个未读数字,而读取的内容仍会溢出一个 32 位整数。如果您在较长的字符串上使用strtoX() 系列函数而不是atoi(),那么它们会检测到诸如溢出之类的问题,而scanf() 和%d 和atoi() 都不会。 (这是主要答案中掩盖的众多观点之一。)
此外,在具有兆字节(通常是千兆字节)主内存的系统上,堆栈上的 4 KiB 不是主要问题。也就是说,我使用 4 KiB 部分是因为它的冲击值;但 POSIX 要求 [LINE_MAX] 的最小值为 2048。
如果您正在阅读基于行的输入,这通常是在命令行应用程序中进行输入的好方法,那么您需要确保阅读整行,因为处理部分行会很混乱并且会导致错误报告难的。 fgets() 加上sscanf() 处理的一个主要优点是您可以在错误报告中使用完整的行,而不是scanf() 在处理部分行后留下的内容。如果第一次尝试失败,您也可以尝试以不同的方式扫描字符串; scanf() 系列中的直接文件 I/O 函数无法做到这一点。
如果您天真地没有认识到人们在您希望他们输入短行时会输入长行,那么您可以将剩菜作为新行 - 无需进一步的用户交互 - 当它实际上是前一行的渣滓时输入。例如,如果您扫描 20 位数字中的 12 位,那么下一个输入将获得剩余的 8 位数字,而无需等待用户输入任何新内容,即使您提示他们输入更多信息。 (另外,请注意using fflush(stdin);它是否有任何用处,充其量是特定于系统的。)
我使用 fgets() 和 4 KiB 缓冲区。如果您想防止程序在单行上发送数兆字节的 JSON 编码数据,则需要使用 POSIX 的 getline() 函数来读取该行。它为整行分配足够的空间,除非内存不足。对于大多数学生的练习作业,4 KiB 缓冲区和fgets() 是一个合理的替代品。只要指数至少为 8,我愿意就使用的 2 的幂进行协商——该值至少为 256。例如,将行缓冲区限制为 80 个字符并不会阻止用户输入超过一行 80 个字符。这只是意味着额外的字符不太可能得到适当的处理。将缓冲区限制为 13 个字符不会给您带来任何有价值的东西,IMO,而且“节省堆栈空间”是一种过早的优化。