c语言问题 int short 转换问题 求解下面short时候为啥i+k《0
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c语言问题 int short 转换问题 求解下面short时候为啥i+k《0相关的知识,希望对你有一定的参考价值。
#include<stdio.h>
int main()
int i = -2 ; // short 时候 i+k<=0
unsigned int k = 1 ;
printf("i+k=%0x\n",i+k); // ffffffff
printf("i+k=%d\n",i+k); // -1
if(i+k>0)
printf("i+k>0\n") ; //i+k>0
else
printf("i+k<=0\n") ;
return 0;
int是-32768~32767,你加上unsigned的之后,负数的部分就都变成正数了,然后使用的都是补码,-1自然就变成最大值了。查查补码和无符号整数的含义你就懂了。 参考技术A 计算机里存储使用补码表示,
i=-2的补码是0xFFFF FFFE
k=1的补码是 0x0000 00001
表达式里计算i+k时,unsigned int精度大于int,int向unsigned int转换,然后再相加,
故i+k=0xFFFF FFFF
上述结果当成不同类型使用时会有不同结果,若为无符号数据,则为0x0FFFF FFFF,若为带符号数据,则为-1
%X直接打16进制,即0x0FFFF FFFF
%d表示带符号数据,即-1,
%u表示无符号数据,即0xFFFF FFFF
表达式里,按运算操作数的最高精度为结果,即unsigned int,故0x0FFFF FFFF追问
我想问的是要是都换成 short型时候 为什么i+k《0 ?
追答根据具体的计算机硬件和编译器的不同,上述类型占用的字节长度不同,一般signed/unsigned int占4字节,signed/unsigned int占用2字节。
signed int或int
unsigned int
signed short或signed short int
unsigned short或unsigned short int
上述四种整数类型是不一样的,int即代表着signed int而不是short int。
全过程只存在带符号signed int与无符号unsigned int类型的转换,转换原则为低精度(范围)向高精度转(大范围),不存在你说的short类型转换。
无符号数据的最高位都是数据,而带符号位最高位为符号位(正为0,负为1),存储是负数从最高位开始肯定是1。
这样一样,
同级别/范围的带符号数向带符号数据赋值的时候,直接填充就可以了,你说的-2赋到int便是直接赋值,你可以去查看编译的汇编代码确认,打印需要%d的时候,也是同级转换;
当低精度带符号数据向高精度带符号位转换的时候,会有一个符号位扩充,即使用最高位的0或1去填充前面新增加的位,进而保持数值的不变。条件表达式里,一个带符号,一个无符号,带符号向无符号转换,最后结果也是无符号。除非你在前面加上(int)的带符号强制类型转换。
当高精度带符号数据向低精度带符号位转换的时候,直接丢弃前面的字节。
而当带符号数据向无符号数据转换的时候,最高位符号位全部直接变成了数据位,打印%X的时候,%X是十六进制,没有符号一说,就是这种转换。
为啥在 C 和 C++ 中的算术运算之前必须将 short 转换为 int?
【中文标题】为啥在 C 和 C++ 中的算术运算之前必须将 short 转换为 int?【英文标题】:Why must a short be converted to an int before arithmetic operations in C and C++?为什么在 C 和 C++ 中的算术运算之前必须将 short 转换为 int? 【发布时间】:2014-06-23 17:30:34 【问题描述】:根据我从this question 得到的答案,似乎 C++ 在从 C 中执行算术运算时继承了将short
转换为int
的要求。我想请你动脑筋为什么首先在 C 中引入?为什么不直接以short
进行这些操作?
例如(取自 dyp 在 cmets 中的建议):
short s = 1, t = 2 ;
auto x = s + t ;
x
的类型为 int。
【问题讨论】:
@Jefffrey 积分提升是通常算术转换的一部分。short s=1, t=2; auto x = s+t;
然后x
是int
。
maxshort + maxshort > maxshort
@technosaurus 无法解释为什么 int
没有提升为 long
(maxint + maxint > maxint)。
我没有得到这个问题的反对意见。这是一个很好的问题,答案很有趣。四次投反对票而没有 cmets 非常令人沮丧。
@dyp:为什么x
是int
类型的规则在 C 和 C++ 中完全不同... ;-)
【参考方案1】:
如果我们查看6.3.1.8
部分中的Rationale for International Standard—Programming Languages—C 通常的算术转换,它会说(强调我的未来):
来自draft C99 standard 的标准中关于这些转换的规则是轻微的 K&R 中的修改:修改适应添加的 类型和值保留规则。 显式许可证已添加到 以比绝对必要的“更广泛”的类型执行计算, 因为这有时会产生更小更快的代码,而不是 更频繁地提及正确答案。计算也可以 只要相同 得到最终结果。显式转换总是可以用来获得 所需类型的值
Section 6.3.1.8 涵盖了应用于算术表达式操作数的常用算术转换,例如6.5.6 Additive operators 部分说:
如果两个操作数都有算术类型,通常的算术 对它们执行转换。
我们在6.5.5 Multiplicative operators 部分也发现了类似的文字。对于 short 操作数,首先从6.3.1.1 Boolean, characters, and integers 部分应用 integer Promotion,该部分表示:
如果一个 int 可以表示原始类型的所有值,则该值为 转换为 int;否则,它将转换为无符号整数。 这些被称为整数促销。48)所有其他类型都是 整数促销保持不变。
Rationale or International Standard-Programming Languages-C 的6.3.1.1
部分关于整数提升 的讨论实际上更有趣,我将选择性地引用 b /c 太长,无法完整引用:
实施分为两大阵营 作为无符号保留和价值保留。
[...]
无符号保留方法要求促进两个较小的 无符号类型为无符号整数。这是一个简单的规则,并产生一个 与执行环境无关的类型。
保值方法要求将这些类型推广到 如果该类型可以正确表示 原始类型,否则用于将这些类型提升为无符号 诠释。因此,如果执行环境将 short 表示为某物 小于 int,unsigned short 变为 int;否则它变成 无符号整数。
正如Inconsistent behaviour of implicit conversion between unsigned and bigger signed types 所展示的那样,在某些情况下,这可能会产生一些相当出乎意料的结果,这样的例子还有很多。尽管在大多数情况下,这会导致操作按预期进行。
【讨论】:
是的,有时它会更小更快,因为您不需要额外的指令来将值符号/零扩展为 int 或屏蔽高位。在 x86 中,您也不需要额外的指令前缀来更改参数大小 太糟糕了,基本原理没有添加第二条规则,如果加法、乘法或按位运算符的结果被强制为小于int
的无符号类型,则表达式的行为就像它的操作数同样被强制,并且操作在较小的类型上执行。没有定义的情况会与这样的规则相矛盾,但是一些编译器可能会以提升为借口来推断像 x*=y;
这样的语句(带有两个变量 unsigned short
)承诺 x
不能超过 2147483648/y。
如果我有类似 int x = 1234
和 char *y = &x
的东西。 1234
的二进制表示是 00000000 00000000 00000100 11010010
。我的机器是小端的,所以它将它反转并存储在内存中11010010 00000100 00000000 00000000
LSB 是第一位的。现在主要部分。如果我使用printf("%d" , *p)
。 printf
将读取第一个字节 11010010
only 输出是 -46
但 11010010
是 210
那么为什么它打印 -46
。我真的很困惑,我猜一些字符到整数的提升正在做一些事情,但我不知道。
您引用了 C99 标准,但这种行为不早于此吗?我要睡觉了,我去看看能不能在 K&R 找到什么。
@PJTrailll 好吧wikipedia 指向c89 的一个版本,尽管您无法获得正式的草稿。在那个版本的通常的算术转换下,它描述了一个非常相似的过程。所以我会说是的。请注意上面的引述对 K&R 中的内容进行了轻微修改,因此 K&R 应该有所不同。【参考方案2】:
这与其说是语言的特性,不如说是对运行代码的物理处理器架构的限制。 C 语言中的 int
类型通常是标准 CPU 寄存器的大小。更多硅片占用更多空间和更多功率,因此在许多情况下只能对“自然大小”数据类型进行算术运算。这并非普遍正确,但大多数架构仍然存在此限制。换句话说,当添加两个 8 位数字时,处理器中实际发生的是某种类型的 32 位算术,然后是简单的位掩码或其他适当的类型转换。
【讨论】:
我不确定是否有位掩码。处理器以其本机字长进行算术运算,然后仅将低位存储回内存。 (此外,虽然您说得对,大多数架构只做字算术,但一个值得注意的例外,英特尔,分布非常广泛。) @JamesKanze 你是对的。我按答案编辑。是的,英特尔在优化算法方面遥遥领先,尤其是使用 IPP 库。 我不同意“这不是语言的特征”;它是语言的一个特征。它是这样定义的,因为......但它是由语言定义的,而不是由处理器定义的。 @JonathanLeffler 这当然是该语言的一个特性。在大多数语言中,我认为。但 Phonon 的回答解释了 为什么 语言具有此功能。 (可能值得指出的是,在过去,机器只有字,而不是字节、半字等。当引入字节寻址时,它只影响内存访问,而不影响寄存器和操作。所以虽然 PDP-11 有字节指令和字指令,当字节指令的目标地址是寄存器时,字节被符号扩展为字。) CPU 执行命令的方式对用户代码完全隐藏。你根本没有回答这个问题。【参考方案3】:short
和 char
类型被标准类型的“存储类型”考虑,即您可以用来节省一些空间但不会为您带来任何速度的子范围,因为它们的大小是“不自然的” " 代表 CPU。
在某些 CPU 上这是不正确的,但是好的编译器足够聪明,可以注意到,如果你例如将常量添加到 unsigned char 并将结果存储回 unsigned char 中,则无需进行 unsigned char -> int
转换。
例如用g++生成的内循环代码
void incbuf(unsigned char *buf, int size)
for (int i=0; i<size; i++)
buf[i] = buf[i] + 1;
只是
.L3:
addb $1, (%rdi,%rax)
addq $1, %rax
cmpl %eax, %esi
jg .L3
.L1:
您可以看到使用了无符号字符加法指令 (addb
)。
如果您在短整数之间进行计算并将结果存储在短整数中,也会发生同样的情况。
【讨论】:
【参考方案4】:链接的问题似乎很好地涵盖了它:CPU 只是没有。 32 位 CPU 为 32 位寄存器设置了本机算术运算。处理器更喜欢以它最喜欢的大小工作,对于这样的操作,将一个小值复制到本机大小的寄存器是很便宜的。 (对于 x86 架构,32 位寄存器被命名为好像它们是 16 位寄存器的扩展版本(eax
到 ax
、ebx
到 bx
等);参见 x86 integer instructions) .
对于一些极其常见的操作,尤其是向量/浮点算术,可能会有专门的指令对不同的寄存器类型或大小进行操作。对于短的东西,用(最多)16位零填充几乎没有性能成本,并且添加专门的指令可能不值得在模具上花费时间或空间(如果你想真正了解原因;我不确定它们是否会占用实际空间,但它确实变得更加复杂)。
【讨论】:
这不仅仅是一个硬件问题,在起草 C99 标准的过程中做出了一个有意识的选择,以使整数提升以一种特定的方式工作。 “请注意,32 位寄存器也被命名为好像它们是 16 位寄存器的扩展版本(eax 到 ax,ebx 到 bx 等)”这对于 x86 是正确的,但是 不正确对于大多数其他架构。无论在 32 位还是 64 位模式下,MIPS 寄存器都具有相同的名称,并且它们始终以本机大小工作,因此无论如何您都不能在 8 位或 16 位中进行算术运算以上是关于c语言问题 int short 转换问题 求解下面short时候为啥i+k《0的主要内容,如果未能解决你的问题,请参考以下文章
c语言中如何将short,int,long,float这些类型的数值转换为字符串?