【发布时间】: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 被显式类型转换为int 的p 递减,而e2 被隐式类型转换的p 递减。
我预计 e1 和 e2 将包含相同的值,但它们没有......事实上,结果看起来取决于系统。
为了测试代码,我有两个虚拟机(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 位引起的,还是与其他原因有关。
但是,我想了解为什么 e1 和 e2 存在差异,以及在这种情况发生时是否至少可以从 GCC 获得警告。
谢谢:-)
【问题讨论】:
-
查看从这两种情况生成的程序集,似乎在显式转换的情况下,
p被转换为整数,然后从e1中减去。在隐式转换的情况下,首先将e2转换为浮点数,然后减去p,然后将结果转换为整数。也许有人可以指出标准中指定的地方......?事实上,Visual C 编译程序的输出类似于 gcc。 -
如果您将
e2 -= p;替换为e2 -= (uint32_t)p;,那么e2和e1的轨迹相同。您有一个问题:在表达式e2 -= p;中,操作正在发生什么类型转换? 根据转换规则,这类似于e2 = e2 - p,它将e2转换为 @987654350 @ 首先,做减法,然后将结果转换回uint32_t。这就留下了一个问题:e2转换为浮点数时会发生什么?。4294967269是一个大整数,并且在单精度的过程中float失去了显着的数字精度。因此,奇怪的结果。 -
命名法:有隐式 conversions 和显式 conversions。 cast 是人类程序员通过使用 cast 运算符有意强制进行显式转换的操作。
-
无论如何,这似乎是一个常见的常见问题解答。我已将链接的帖子作为规范副本添加到 SO C FAQ。