【问题标题】:Unexpected result of implicit typecast on gcc [duplicate]gcc上隐式类型转换的意外结果[重复]
【发布时间】:2018-07-09 10:28:00
【问题描述】:

正如标题中所述,关于 Linux 上的 GCC 上的显式与隐式类型转换,我遇到了一件非常奇怪的事情。

我有以下简单的代码来演示这个问题:

#include <stdio.h>
#include <stdint.h>

int main(void)
{
   int i;
   uint32_t e1 = 0;
   uint32_t e2 = 0;
   const float p = 27.7777;

   printf("#   e1 (unsigned)   e1 (signed)       e2 (unsigned)   e2 (signed)\n");
   for (i = 0; i < 10; i++) {
      printf("%d   %13u   %11d       %13u   %11d\n", i, e1, e1, e2, e2);
      e1 -= (int)p;
      e2 -= p;
   }
   return 0;
}

如您所见,e1 被显式类型转换为intp 递减,而e2 被隐式类型转换的p 递减。

我预计 e1e2 将包含相同的值,但它们没有......事实上,结果看起来取决于系统。

为了测试代码,我有两个虚拟机(VirtualBox 从 Vagrant 开始)。这是第一台机器:

vagrant@vagrant:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.3 LTS
Release:        16.04
Codename:       xenial
vagrant@vagrant:~$ uname -a
Linux vagrant 4.4.0-92-generic #115-Ubuntu SMP Thu Aug 10 16:02:55 UTC 2017 i686 i686 i686 GNU/Linux
vagrant@vagrant:~$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

要构建和执行,我使用以下内容

vagrant@vagrant:~$ gcc -Wall /vagrant/test.c
vagrant@vagrant:~$ ./a.out
#   e1 (unsigned)   e1 (signed)       e2 (unsigned)   e2 (signed)
0               0             0                   0             0
1      4294967269           -27          4294967269           -27
2      4294967242           -54          4294967268           -28
3      4294967215           -81          4294967268           -28
4      4294967188          -108          4294967268           -28
5      4294967161          -135          4294967268           -28
6      4294967134          -162          4294967268           -28
7      4294967107          -189          4294967268           -28
8      4294967080          -216          4294967268           -28
9      4294967053          -243          4294967268           -28
vagrant@vagrant:~$

正如您所见,e1 使用显式类型转换的一切看起来都很好,但对于 e2,结果很奇怪......

然后我在另一个 64 位版本的 Ubuntu 虚拟机上尝试相同的操作:

vagrant@ubuntu-xenial:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.3 LTS
Release:        16.04
Codename:       xenial
vagrant@ubuntu-xenial:~$ uname -a
Linux ubuntu-xenial 4.4.0-112-generic #135-Ubuntu SMP Fri Jan 19 11:48:36 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
vagrant@ubuntu-xenial:~$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

这是应用程序的输出:

vagrant@ubuntu-xenial:~$ gcc -Wall /vagrant/test.c
vagrant@ubuntu-xenial:~$ ./a.out
#   e1 (unsigned)   e1 (signed)       e2 (unsigned)   e2 (signed)
0               0             0                   0             0
1      4294967269           -27          4294967269           -27
2      4294967242           -54                   0             0
3      4294967215           -81          4294967269           -27
4      4294967188          -108                   0             0
5      4294967161          -135          4294967269           -27
6      4294967134          -162                   0             0
7      4294967107          -189          4294967269           -27
8      4294967080          -216                   0             0
9      4294967053          -243          4294967269           -27

e2 的值仍然不是我所期望的,但现在与我在 32 位系统上的值不同。

我不知道这种差异是由 32 位和 64 位引起的,还是与其他原因有关。

但是,我想了解为什么 e1e2 存在差异,以及在这种情况发生时是否至少可以从 GCC 获得警告。

谢谢:-)

【问题讨论】:

  • 查看从这两种情况生成的程序集,似乎在显式转换的情况下,p 被转换为整数,然后从e1 中减去。在隐式转换的情况下,首先将e2 转换为浮点数,然后减去p,然后将结果转换为整数。也许有人可以指出标准中指定的地方......?事实上,Visual C 编译程序的输出类似于 gcc。
  • 如果您将e2 -= p; 替换为e2 -= (uint32_t)p;,那么e2e1 的轨迹相同。您有一个问题:在表达式 e2 -= p; 中,操作正在发生什么类型转换? 根据转换规则,这类似于 e2 = e2 - p,它将 e2 转换为 @987654350 @ 首先,做减法,然后将结果转换回uint32_t。这就留下了一个问题:e2 转换为浮点数时会发生什么?4294967269 是一个大整数,并且在单精度的过程中 float 失去了显着的数字精度。因此,奇怪的结果。
  • 命名法:有隐式 conversions 和显式 conversionscast 是人类程序员通过使用 cast 运算符有意强制进行显式转换的操作。
  • 无论如何,这似乎是一个常见的常见问题解答。我已将链接的帖子作为规范副本添加到 SO C FAQ

标签: c gcc


【解决方案1】:

语句e2 -= p 等价于e2 = e2 - p。由于e2 - p 是一个涉及float (e2 = unsigned_val - float_val) 的减法运算,因此该表达式的计算结果为float,这意味着您实际上是在将float 值赋给一个无符号整数变量。

另一个语句e1 -= (int) p 不等价,因为这里发生的是e1 = unsigned_val - int_val,所以分配给e1 的值是整数而不是浮点数。

由于将浮点值分配给超出整数类型范围的整数是未定义的行为(请参阅 C 标准的 6.3.1.4 实浮点数和整数),您分配给e2 后,在不同平台上遇到不同的行为。

【讨论】:

  • 感谢您的解释!现在是有道理的,而且我可以看到这种情况不可能得到警告。
猜你喜欢
  • 1970-01-01
  • 2021-10-27
  • 1970-01-01
  • 2011-03-24
  • 2020-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多