C++ 无符号和有符号int和printf

C++ 无符号和有符号int和printf,c++,c,C++,C,我知道我正在给signed int分配一个大于它所能处理的值。另外,我应该使用%d表示已签名,使用%u表示未签名。类似地,我不应该将-ve值赋给unsigned。但如果我做了这样的作业并使用如下printf,我会得到如下结果 我的理解是,在每种情况下,数字转换为其2的互补二进制表示,这与-1或4294967295相同。这就是为什么%u通过忽略-ve最左边的位来为签名打印4294967295。当将%d用于有符号整数时,它使用最左边的位作为-ve标志并打印-1。类似地,unsigned的%u会打印u

我知道我正在给signed int分配一个大于它所能处理的值。另外,我应该使用
%d
表示已签名,使用
%u
表示未签名。类似地,我不应该将
-ve
值赋给unsigned。但如果我做了这样的作业并使用如下printf,我会得到如下结果

我的理解是,在每种情况下,数字转换为其2的互补二进制表示,这与
-1
4294967295
相同。这就是为什么
%u
通过忽略
-ve
最左边的位来为签名打印
4294967295
。当将%d用于有符号整数时,它使用最左边的位作为
-ve
标志并打印
-1
。类似地,unsigned的
%u
会打印unsigned值,但
%d
会将数字视为有符号,从而打印
-1
。对吗

signed int si = 4294967295;
unsigned int  ui = 4294967295;

printf("si = u=%u d=%d\n", si, si);
printf("ui = u=%u d=%d\n", ui, ui);
输出:

si = u=4294967295 d=-1
ui = u=4294967295 d=-1
si = u=4294967295 d=-1
ui = u=4294967295 d=-1

输出:

si = u=4294967295 d=-1
ui = u=4294967295 d=-1
si = u=4294967295 d=-1
ui = u=4294967295 d=-1

这里发生了一些事情,让我们首先说,使用不正确的格式说明符来
printf
是未定义的行为,这意味着程序的结果是不可预测的,实际发生的情况将取决于许多因素,包括编译器、体系结构、优化级别等

对于由相应的标准定义的符号/非符号转换,C和C++都实现了实现定义的行为,以转换大于符号整数类型的值,从C++草稿标准:

如果目标类型是有符号的,则如果可以在目标类型(和)中表示该值,则该值将保持不变 位域宽度);否则,该值由实现定义

例如,
gcc
选择使用相同的:

对于转换为宽度为N的类型,该值以2^N的模减小,以在该类型的范围内;没有发出任何信号

当你将代码> -1 < /C> >在C和C++中的无符号值时,结果将始终是该类型的最大未签名值,从草案C++标准:

如果目标类型为无符号,则结果值最小 与源整数全等的无符号整数(模2n,其中n为 用于表示无符号类型的位数)。[注:在 二的补语表示法,这种转换是概念上的和逻辑上的 位模式没有变化(如果没有截断)。 -[完注]

C99中的措辞更容易理解:

否则,如果新类型是无符号的,则该值将由 重复地加上或减去一个大于最大值 可以在新类型中表示,直到值在 新型的

因此,我们有以下几点:

-1 + (UNSIGNED_MAX + 1)
其结果是
UNSIGNED_MAX

至于
printf
和不正确的格式说明符,我们可以从C99标准草案的
7.19.6.1
部分看到,fprintf函数说明:

如果转换规范无效,则行为无效 未定义。248)如果任何参数不是 对应的转换规范,行为未定义

<>代码> fPrtutf关于格式说明符覆盖<代码> PROTFF < /C> > C++中关于<代码> Prtff>的下落。

这就是为什么在4294967295签名打印时忽略-ve最左端位的原因。当将%d用于有符号整数时,它使用最左边的位作为-ve标志并打印-1

在无符号情况下,“最左边”或最高有效位不被忽略,且不为负;相反,它的位置值为231

在负的情况下,符号位不是标志;相反,它是一个位置值为-231的位

在这两种情况下,整数的值等于设置为1的所有二进制数字(位)的位值之和


以这种方式对有符号值进行编码称为。它不是唯一可能的编码方式;例如,你所描述的是符号和大小,一个人的补语是另一种可能性。然而,在实践中很少遇到这些替代编码,这不仅是因为二者的互补性是算术在现代硬件上的工作原理,但可能是最神秘的架构。

请注意,我对这个问题的回答解释了。。。因此,将
-1
赋值给无符号值始终是定义良好的行为。但仅仅将负整数值重新解释为
无符号的
整数值并不是定义良好的。正如将太大的
无符号
整数值重新解释为
有符号
整数值一样,您可能会发现“std::numeric_limits”很有用。从uin32_t更改为uint64_t更具可读性。std::numeric_limits::max(),变为std::numeric_limits::max()等等。@Deduplicator你是说在
printf
的上下文中?@Deduplicator我在
printf
上下文中添加了使其成为UB的引号,并且无论在优化下转换是否会做坏事,编译器都可以用UB做任何事情。不要忘记变量函数的措辞,也就是说,传递的是
signed
还是
unsigned
整数类型的值并不重要,只要该值在这两种类型中都是可表示的。我发现,通过查看这种情况的1字节等效值,可以更容易地将其可视化。设置8位时,作为无符号位模式,其值通常为255。作为2s补码有符号值,底部7位的值为127,但顶部位的值为-128,产生的有符号值为-1。类似的情况也发生在2、4和8字节的值上:所有1位模式的值为-1作为有符号整数。只是更容易勾勒出并掌握8位的位模式…@Razzle肯定更容易,但这不是问题所在。如果你觉得自己的答案增加了价值(很可能是这样),请随意发布。