【问题标题】:Value of boolean type changed in C布尔类型的值在 C 中更改
【发布时间】:2018-02-09 07:30:03
【问题描述】:

我注意到在 C 中,我的布尔变量以某种我不理解的方式发生了变化。

#include <stdio.h>
#include <stdbool.h>

int main(void) {
   bool x, y;

   printf("x: ");
   scanf("%d", &x);

   printf("x is %d\n", x);

   printf("y: ");
   scanf("%d", &y);

   printf("x is %d\n", x);
   printf("y is %d\n", y);

   return 0;
}

如果我为x 输入一个值1 并为y 输入任何值(在此示例中为1):

x: 1
x is 1
y: 1
x is 0
y is 1

最后,y 输出正确的原始值,但 x 在两者之间神奇地变为0

x 的输入是0 时,这不是问题,因为xy 的输出都是它们各自的预期值。

请解释发生了什么!

【问题讨论】:

  • 旁注:始终检查 scanf 函数的返回值。否则,当用户提供无效(在本例中为非数字)输入时,您将遇到 未定义的行为
  • 节省时间,启用编译器警告。 bool x, y; ... scanf("%d", &amp;x); 应该警告过你。
  • @chux 我同意,我添加了这个作为答案。

标签: c boolean scanf


【解决方案1】:

您将布尔变量的地址传递给scanf(),它需要int* 类型的变量。这将调用未定义的行为,您可能会得到错误的结果甚至崩溃。

要解决此问题,请使用临时int 存储对布尔值(作为 int)的扫描,然后将其存储到布尔变量中。

演示

bool x, y;
int tmp;

printf("x: ");
scanf("%d", &tmp);
x = tmp;

另一方面,打印布尔变量是不同的故事,布尔值被protomoted为int而没有任何问题并正确打印。

【讨论】:

  • x = (tmp != 0 ? true : false); z-> x = !!tmp;
【解决方案2】:

bool 不是int。使用 %d 格式说明符读取 int 是未定义的行为。

根据 7.21.6.2 fscanf 函数,第 13 段 of the C standard

如果转换规范无效,则行为未定义。

请注意,7.21.6.1 fprintf 函数的第 9 段指出:

如果转换规范无效,则行为是 不明确的。如果任何参数不是正确的类型 相应的转换规范,行为是 未定义。

但这是针对fprintf(),而不是fscanf()scanf() 函数的格式说明符要严格得多,因为不会有任何参数提升 允许%d 等格式“工作”charbool,在printf() 调用中升级为intscanf() 函数传递参数的地址,如果地址所指的大小与格式说明符所期望的大小不符,则会导致未定义的行为 - 例如无法解释的更改另一个变量。

【讨论】:

  • 这是错误。此处的转换规范并非无效。见this
  • @SouravGhosh 你怎么知道的?
  • @PaulR 不,先生,转换规范很好,这是不匹配的参数。
  • @SouravGhosh 嗯?显然人们的意思是“转换说明符对于提供的参数是错误的”
  • 阅读fscanf 文档,我仍然认为可能存在问题。我认为您应该引用第 10 段,特别是 "...如果此对象没有适当的类型,或者...,则行为未定义"。我相信第 13 段仅涵盖格式错误的转换说明符语法(例如不存在的 %k)。
【解决方案3】:

好的,这里有两点。

  1. bool 的大小由实现定义。
  2. 标准中没有为bool 类型定义格式说明符。

因此,虽然 扫描 值,但将 bool 的地址作为 %d 的参数传递是不好的请参阅注释,因为提供的类型不是与预期类型相同。

您可以使用中间整数,将值扫描到该整数中,然后(在验证或转换为truefalse MACRO 之后)将结果分配回bool 类型变量。

但是,对于打印,由于默认参数提升,bool 可以毫无问题地成为%d 参数的候选者。


注意:

%d*scanf() 期望参数是 int *,而不是提供 bool* 将导致 undefined behavior

相关,引自章节 §7.21.6.2,第 10 段

[....] 除非* 指示分配抑制,否则 转换的结果放在后面的第一个参数指向的对象中 尚未收到转换结果的format 参数。 如果这个对象 没有合适的类型,或者转换的结果无法表示 在对象中,行为是未定义的。

【讨论】:

  • 您能否详细解释一下“将布尔地址作为%d 的参数传递是不好的”?
  • 这个可能有帮助:printf("\n_Bool: %zu\nint: %zu", sizeof(_Bool), sizeof(int)); 表明它们的大小可能不同。在我的机器(gcc 4.8.5)上,我得到:_Bool: 1int: 4
  • @AndreKampling 没有太大的相关性,因为即使对于具有相同大小的两种类型,使用不匹配的格式说明符仍然是未定义的行为
  • @M.M 扩展为intfloat。对齐方式也很重要。
  • @M.M 也,编辑了最后一部分以使其更清晰,更好吗?
【解决方案4】:

bool 是与int 不同的数据类型,它很可能是单个字节。

scanf 不是类型安全的函数,你用 %d 转换说明符告诉它 指向 int 的指针,而 scanf 无法知道你有通过 pointer to bool 而不是 指向 int 的指针。然后,您将获得未定义的行为

clang 编译器生成警告:

source_file.c:8:16: warning: format specifies type 'int *' but the argument has type 'bool *' [-Wformat]
   scanf("%d", &x);
          ~~   ^~
source_file.c:13:16: warning: format specifies type 'int *' but the argument has type 'bool *' [-Wformat]
   scanf("%d", &y);
          ~~   ^~

【讨论】:

    【解决方案5】:

    所有其他答案在技术上都是正确的,但并不能真正帮助您。

    您的主要问题不是 x 也不是 y,而是您无法自己调试最琐碎的编程问题。这很容易修复 - 启用编译器警告(如果使用 gcc,请添加 -Wall 选项)。编译器会通知你这样的错误,还有很多很多。不需要每次都来stackoverflow :)

    【讨论】:

      【解决方案6】:

      考虑以下程序

      #include <stdio.h>
      #include <stdbool.h>
      
      int main(void) {
        int z, s;
        printf("int  z: %p\nint  s: %p\n", &z, &s);
      
        bool x, y;
        printf("bool x: %p\nbool y: %p\n", &x, &y);
      
        return 0;
      }
      

      输出:

      int  z: 0028FF3C
      int  s: 0028FF38
      bool x: 0028FF37
      bool y: 0028FF36
      

      现在,zs 之间有 4 个字节的差异,在我的例子中是 int 的大小。

      xy 之间只有 1 个字节的差异。

      现在用这一行:

      scanf("%d", &y);
      

      您通过%dscanf 告诉您需要一个4 字节长的输入,即int

      这个int 将被存储在y 的情况下来自地址0028FF36 并且输入0x0001 的第二个字节是0 将存储在0028FF37。那就是x的地址。

      您可以使用另一个输入值来检查它。例如,如果您将65535 分配给y,即0xFFFF,那么x 将是0xF,即255 十进制。

      【讨论】:

      • 你知道sizeof吗?
      • @M.M 我发现这种方式更能说明问题。
      【解决方案7】:

      或者可能(如果输入字符串包含 true 或为 1 将返回 1 - 否则 0

      int getBool(char *message)
      {
          char input[20];
          printf("%s", message);
          fgets(input, 20, stdin);
          return !!strstr(strlwr(input), "true") || strtol(input, NULL, 10);
      }
      

      【讨论】:

      • 没有!!,返回值是一样的。我猜是风格问题。
      • 在这种情况下是的
      猜你喜欢
      • 1970-01-01
      • 2014-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-25
      • 1970-01-01
      • 2010-09-13
      • 2022-01-15
      相关资源
      最近更新 更多