为什么 scanf 接受“%f”格式说明符的字符?

Why scanf accepts chars for "%f" format specifier?

我正在为我的 类 准备一个简单的程序,发现 scanf 并不像我预期的那么严格:

int main()
{
  // Define variables
  float alpha;
  float b;
  float result;

  // Get user input
  printf("Type alpha and b. Space separated float numbers only.\n");
  scanf("%f %f", &alpha, &b);
  printf("%f %f\n", alpha, b);
  return 0;
}

Returns:

Type alpha and b. Space separated float numbers only.
a 0
-29768832.000000 0.000000

我在哪里可以了解它的行为?

如果既没有整数也没有浮点数,我如何使标准函数的输入严格并抛出异常?

P. S. 我在 Ubuntu.

中使用 gcc v5

I'm preparing a simple program for my classes and found that scanf is not as strict as I expected

不,你没有。

鉴于此 scanf 调用:

  float alpha;
  float b;

  scanf("%f %f", &alpha, &b);

... 并且此输入:

a 0

... scanf 在尝试扫描浮点值且尚未扫描任何十进制数字时遇到 'a' 时匹配失败。它在该点停止(并在流中留下 'a' 以供后续读取),并且一如既往,它 return 是成功扫描的输入字段数 (0)。

Where I can read about its behavior?

您可以在命令行(Linux、OSX、...)尝试 运行 命令 man scanf,或输入与 Google 搜索。或者 google "scanf docs" 或 "scanf manual" 或类似的。或者查找您的特定 C 库的官方手册。对于标准库函数,the standard 是一个很好的参考,但您的特定实现可能有扩展,那里没有记录,或者有时甚至可能在细微的方面与标准不同。

How can I make input strict with standard functions and throw an exception if there was neither ints nor floats?

C 没有例外。大多数情况下,它通过函数 return 值告诉您有关错误的信息,尽管某些第三方库的做法可能略有不同。但是您必须通过检查 return 值并适当地响应来完成 您的 部分。在这种特殊情况下,您必须避免读取 alphab 的值。因为这些变量没有被初始化并且随后没有分配任何值,读取它们的值会产生 未定义的行为.

其他答案,特别是很好地解释了为什么代码无法达到目标。

注意输入可能会转换为 intfloat,两者都不是,或两者都是。

How can I make input strict with standard functions and throw an exception if there was neither ints nor floats?

最好是使用其他工具如fgets()读取输入并形成字符串以备后用测试。

严格测试有点难。 *scanf("%d",...) 在溢出时有 未定义的行为 *scanf("%f",...)有类似的UB,尤其是当float不支持infinity/not-a-number时。


测试字符串是否转换为有效的int

bool my_isint(const char *s) {
  // Add to dis-allow leading white-space
  if (isspace((unsigned char) *s)) return false;

  char *endptr; 
  base = 0; // use base = 0 to only allow base 10 input
  errno = 0;
  long val = strtol(s, &endptr, base); 
  if (s == endptr) return false; // no conversion

  if (errno == ERANGE) return false; // too big for long
  if (val > INT_MAX || val < INT_MIN) return false; // too big for int

  // Add to allow trailing white-space
  while (isspace((unsigned char) *endptr)) endptr++;

  // Add to dis-allow trailing junk
  while (*endptr) return false; // trailing junk

  return true;
}

测试字符串是否转换为有效的 float 就像在 float 支持 infinity/non-a-number 时测试 double。稍后- GTG.

As @John Bollinger () and @chux () 请解释问题本身是错误的,实际上 scanf 不接受字符而不是浮点数,而是忽略了它。为了捕获此行为,我按以下方式修改了代码:

+ #include <errno.h> // Error codes constants

int main()
{
  // Define variables
  float alpha;
  float b;
+ int matches;
  float result;

  // Get user input
  printf("Type alpha and b. Space separated float numbers only.\n");
± matches = scanf("%f %f", &alpha, &b);

+ if (matches != 2) {
+   printf("ERROR: Space separated float numbers only expected.\n");
+   return EINVAL;
+ }

  printf("%f %f\n", alpha, b);
  return 0;
}

仅适用于您在 CS 学位期间编写的简单学习程序!

否则,请检查 答案以获得更类似于生产的解决方案。