提问人:Tom 提问时间:8/31/2023 最后编辑:Tom 更新时间:9/1/2023 访问量:142
无符号长整型 resTestBad = 0xffffffff + 1;使用 %llu 打印 0,但无符号长长 resA = 4294967295 + 1;使用 %llu 打印4294967296
unsigned long long resTestBad = 0xffffffff + 1; print 0 with %llu, but unsigned long long resA = 4294967295 + 1; print 4294967296 with %llu
问:
似乎我在打印 with 的值时发现了一个错误,请参见以下代码:(0xffffffff + 1)
%llu
0
unsigned long long resTestBad = 0xffffffff + 1; // number of f is 8
printf("resTestBad is %llu, sizeof(unsigned long long) is %ld\n", resTestBad, sizeof(unsigned long long));
将输出:
resTestBad is 0, sizeof(unsigned long long) is 8
我认为输出是错误的,它应该是输出或说正确的输出:
resTestBad is 4294967296, sizeof(unsigned long long) is 8
和
unsigned long long resA = 4294967295 + 1;
printf("resA is %llu, sizeof(unsigned long long) is %zu\n", resA, sizeof(unsigned long long));
将输出:
resA is 4294967296, sizeof(unsigned long long) is 8
但下面的代码没有问题:
unsigned long long resTestAgain = 0xfffffffff + 1; // number of f is 9
printf("resTestAgain is %llu, sizeof(unsigned long long) is %zu\n", resTestAgain, sizeof(unsigned long long));
它将输出(我认为是对的):
resTestAgain is 68719476736, sizeof(unsigned long long) is 8
我的 .c 文件如下:
#include <stdio.h>
#include <limits.h>
int main(void)
{
int s = 0xfffe;
printf("s is %d\n", s);
unsigned int ww = 0xfffe;
printf("ww is %d\n", ww);
unsigned long long resTestOk = 0xfffffffe + 1;
printf("resTestOk is %llu, sizeof(unsigned long long) is %zu\n", resTestOk, sizeof(unsigned long long));
unsigned long long resTestBad = 0xffffffff + 1;
printf("resTestBad is %llu, sizeof(unsigned long long) is %zu\n", resTestBad, sizeof(unsigned long long));
unsigned long long resTestAgain = 0xfffffffff + 1;
printf("resTestAgain is %llu, sizeof(unsigned long long) is %zu\n", resTestAgain, sizeof(unsigned long long));
unsigned long long resA = 4294967295 + 1;
printf("resA is %llu, sizeof(unsigned long long) is %zu\n", resA, sizeof(unsigned long long));
unsigned long long resTestUUU = 0xffffffffffffff + 1;
printf("resTestUUU is %llu, sizeof(unsigned long long) is %zu\n", resTestUUU, sizeof(unsigned long long));
unsigned long long resH = (0xffffffff+1) / 1024 / 1024 / 1024;
printf("%llu\n", resH);
unsigned long long resD = (4294967296) / 1024 / 1024 / 1024;
printf("%llu\n", resD);
return 0;
}
运行它将输出:
s is 65534
ww is 65534
resTestOk is 4294967295, sizeof(unsigned long long) is 8
resTestBad is 0, sizeof(unsigned long long) is 8
resTestAgain is 68719476736, sizeof(unsigned long long) is 8
resA is 4294967296, sizeof(unsigned long long) is 8
resTestUUU is 72057594037927936, sizeof(unsigned long long) is 8
0
4
也许你不相信,我也不相信,所以我录制了一个视频,为了证明我说。
查看 https://imgur.com/a/RAQVPxS
是CPU的Bug还是VMWare Workstation的Bug?我该如何解决?
更新
对不起,可能是我的问题不清楚,或者说让人感到困惑。
问题的根源是我想测试一台 32 位计算机可以访问多少内存(我知道是 4GB,地址从 0x00000000 到 0xffffffff,总数是 0xffffffff + 1,单位是字节,所以 / 1024 /1024 / 1024 = 4GB),所以我使用下面的代码:(0xffffffff + 1)
unsigned long long resH = (0xffffffff + 1) / 1024 / 1024 / 1024;
printf("%llu\n", resH);
但是它输出的数字,那不是我想要的。除输出外,I 为 4。所以我也使用下面的代码:0
unsigned long long resQ = (4294967295 + 1) / 1024 / 1024 / 1024;
printf("%llu\n", resQ);
它输出 4 我除了什么。所以我想找出导致结果不同的原因。
然后我使用以下代码:
unsigned long long resTestBad = 0xffffffff + 1;
printf("resTestBad is %llu, sizeof(unsigned long long) is %zu\n", resTestBad, sizeof(unsigned long long));
它输出:
resTestBad is 0, sizeof(unsigned long long) is 8
这不是我的例外,所以我测试了以下代码:
unsigned long long resTestAgain = 0xfffffffff + 1;
printf("resTestAgain is %llu, sizeof(unsigned long long) is %zu\n", resTestAgain, sizeof(unsigned long long));
它输出:
resTestAgain is 68719476736, sizeof(unsigned long long) is 8
如下代码所示:
unsigned long long resA = 4294967295 + 1;
printf("resA is %llu, sizeof(unsigned long long) is %zu\n", resA, sizeof(unsigned long long));
它输出:
resA is 4294967296, sizeof(unsigned long long) is 8
通过查看答案,现在我明白为什么 0xffffffff + 1 输出 0。
但是现在我无法理解 0xffffffff 和 4294967295 的值大小是一样的,为什么0xffffffff无符号 int
类型(占用 32 位),但是4294967295可能是长
或长或其他
类型(占用 64 位)。
答:
正如 @rici 对另一个问题的回答(关于 C++,但相同的规则适用于 C)中所解释的那样,无符号类型是十六进制整数文字的候选者。 适合 32 位 或 ,因此其中之一就是它的类型。然后 转换为 加法,溢出。最后,将 转换为 成为 的初始值。0xffffffff
unsigned int
unsigned long
int
1
unsigned long/int
unsigned long/int
0
unsigned long long
resTestBad
用于从一开始就强制文本。0xffffffffULL
unsigned long long
(当您添加另一个 时,该值不再适合 ,因此文本已经具有类型并且它有效。f
unsigned long
long long
评论
0xffffffff
float
0xffffffff
float
0xffffffff
float
对于初学者来说,最好使用转换说明符来输出类型的值,而不是像您所做的那样:%zu
size_t
%ld
printf("resTestOk is %llu, sizeof(unsigned long long) is %ld\n", resTestOk, sizeof(unsigned long long));
首先,虽然类型通常表示类型的别名,但根据 C 标准(7.19 通用定义 <stddef.h>)size_t
unsigned long
4 用于 size_t 和 ptrdiff_t 的类型不应包含整数 转换排名大于有符号长整型 int 的转换排名,除非 实现支持足够大的对象,使这成为必要。
在您的系统中似乎确实是该类型的别名,并且输出的值可以在有符号类型的对象中表示。size_t
unsigned long
long int
现在让我们考虑一下这个声明的例子:
unsigned long long resTestBad = 0xffffffff + 1;
无符号十六进制常量可以用 类型的对象表示。所以它有类型。因此,在这个表达式中:0xffffffff
unsigned int
unsigned int
0xffffffff + 1
该类型的对象使用算术(由于通常的算术转换,整数常数也转换为类型)。unsigned int
1
unsigned int
结果出现溢出,因为结果不能在 类型的对象中表示。表达式的值为 。unsigned int
0
以下代码片段中也会出现相同的情况:
unsigned long long resH = (0xffffffff+1) / 1024 / 1024 / 1024;
printf("%llu\n", resH);
其中,该类型的表达式为该类型的对象生成溢出并生成 。0xffffffff+1
unsigned int
0
至于这个声明:
unsigned long long resTestAgain = 0xfffffffff + 1;
则十六进制常量不能在 unsigned int 类型的对象中表示(前提是该对象不大于 )。如果等于 或 如果它等于但等于 (在你的程序中它确实等于 ),那么常量可以表示在 类型的对象中。或。0xfffffffff
sizeof( unsigned int )
4
sizeof( long int )
8
4
sizeof( long long int )
8
8
long int
long long int
所以在这个表达中:
0xfffffffff + 1
对 或 类型的对象使用算术。表达式的结果也可以在该类型的对象中表示。long int
long long int
至于这个代码片段:
unsigned long long resA = 4294967295 + 1;
printf("resA is %llu, sizeof(unsigned long long) is %ld\n", resA, sizeof(unsigned long long));
则常数大于 等于 的值。因此,它的类型 if 大于 or 否则。表达式的结果可以将该类型的 n 个对象表示为正值。4294967295
INT_MAX
2147483647
long int
sizeof( long int )
sizeof( int )
long long int
4294967295 + 1
请注意以下 C 标准(6.4.4.1 整数常量)中关于整数常量类型的引用
5 整数常量的类型是相应的 可以在其中表示其值的列表。
Octal or Hexadecimal
Suffix Decimal Constant Constant
===============================================================
none int int
long int unsigned int
long long int long int
unsigned long int
long long int
unsigned long long int
================================================================
u or U unsigned int unsigned int
unsigned long int unsigned long int
unsigned long long int unsigned long long int
================================================================
l or L long int long int
long long int unsigned long int
long long int
unsigned long long int
================================================================
ll or LL long long int long long int
unsigned long long int
================================================================
Both u or U unsigned long long int unsigned long long int
and ll or LL
=================================================================
评论
float f = 5/2;
2.5
=
size_t = unsigned long
size_t
%ld
%ld
此答案假定 int
为 32 位(这是最常见的大小)
是CPU的Bug还是VMWare Workstation的Bug?
不,这不是一个错误。这就是 C 标准所说的必须完成的方式。
做的时候
unsigned long long resTestBad = 0xffffffff + 1;
第一步是计算
0xffffffff + 1;
为此,编译器需要确保两端的操作数具有相同的类型。因此,首先需要弄清楚类型。+
0xffffffff
是十进制的值4294967295不能存储在 32 位有符号值中。但是,它可以存储在 32 位无符号值中,并在源代码中以十六进制写入。(正如 @Lundin 的回答所指出的,在源代码中只会选择有符号类型,这与十六进制文字不考虑可能性不同。因此,它将是 64 位有符号类型,或者取决于是否是 32 位的 64。您可以通过打印 vs. 自行检查。4294967295
unsigned int
long
long long
long
sizeof(4294967295)
sizeof(0xffffffff)
1
具有类型,可以隐式转换为 ,因此编译器选择 32 位无符号进行计算。int
unsigned int
因此,计算按以下方式完成:
(unsigned int)0xffffffff + (unsigned int)1;
这给最终结果零提供了(明确定义的)溢出。
所以
unsigned long long resTestBad = 0xffffffff + 1;
真的是一样的
unsigned long long resTestBad = (unsigned int)0;
请注意,左侧的类型对右侧的计算完全没有影响。不管你写 or or ,右手边的计算都是一样的。它将永远是:=
unsigned long long resTestBad
float resTestBad
double resTestBad
SomeType resTestBad = (unsigned int)0;
顺便说一句:试试这个:
float f = 5/2;
你会因为你在左手边而得到吗?不,除法仍然是整数除法,因此结果是 。2.5
float
(int)2
当您使用 9 而不是 8 时,该值不能再以 32 位类型保存,因此编译器会选择 64 位类型进行计算。因此,不会出现溢出。f
f
我该如何解决?
通过强制以 64 位完成右侧计算。例如:
0xffffffffULL + 1
评论
0xffffffff
4294967295
sizeof(4294967295)
sizeof(0xffffffff)
int
(unsigned long long) 0xffffffff + 1
0xffffffffULL + 1
4294967295ULL + 1
(unsigned long long) 0xffffffff
0xffffffffULL
4294967295ULL
- C 语言中的一切都有一个类型,包括这些东西:它们被称为整数常量。
1
- 通常,整数常量的类型为 。或者,除非它太大而无法放入 ,否则它有类型,或者如果仍然不够大,则 。在所有这些情况下,它都是有符号类型。
int
int
long
long long
- 十六进制(和八进制)整数常量是特殊的。如果一个十六进制整数常数不能容纳在 中,它将变成 ,或者如果它也不能容纳在十六进制整数常数中,则 、 则以此类推。
int
unsigned int
long
unsigned long
所以在 32 位系统上,然后会得到类型,并且会得到类型。两者都不是足够大的类型来存储结果,因此我们已经在这里找到了错误。int
0xffffffff
unsigned int
1
int
0xffffffff + 1
现在碰巧的是,如果您有一个 type 操作数和一个 signed 操作数,则隐式转换(通常的算术转换)会将 signed 类型转换为无符号类型。详细信息:隐式类型升级规则unsigned int
int
因此,两个操作数都结束,加法的结果也将是 。的结果在 C 中定义明确,在 32 位系统上,我们将得到一个环绕,结果是 。unsigned int
unsigned int
(unsigned int)0xffffffff + (unsigned int)1
int
0
从那里开始,您在程序的其余部分使用什么类型并不重要,因为结果已经计算出来了。
结论:在 C 中执行任何操作的类型与给定运算符的操作数有关,与稍后存储结果的赋值的左操作数无关。
出于同样的原因,如果您在纸上写下 1+1=2,然后将该纸存储在文件夹中,纸上的方程式不会根据您存储的文件夹类型而神奇地改变 - 它已经执行了。
评论
(unsigned int)0xffffffff + (unsigned int)1
4294967295 + 1
4294967295
0xffffffff
auto
评论
0xffffffffULL + 1ULL
size_t
sizeof
%zu
unsigned long long resTestAgain = 0xfffffffff + 1; // number of f is 8
不。。。。再数一遍%llu
可以。但不适合unsigned long long
%ld
sizeof
=