如果我们比较 unsigned short int 和 unsigned char(比较位)会发生什么?(C语言)

what happens if we compare unsigned short int and unsigned char (comparing the bits)? (in C language)

提问人:Pentakorr 提问时间:6/22/2023 最后编辑:Vlad from MoscowPentakorr 更新时间:6/23/2023 访问量:89

问:

例如:

    unsigned char mask1 = 0x55; //01010101
    unsigned short int mask2 = 0x8055;//1000000001010101   
    unsigned short int res = 0; //0000000000000000
    res = mask1 | mask2;

那么现在的比特是什么?res

它从 2 个字节转换为 4 个字节吗?而“空白”会是零吗?mask1

我的意思是,从逻辑上讲,它会这样工作吗?

res = mask1 | mask2 = 01010101 | 1000000001010101 

  0000000001010101
| 1000000001010101
  ----------------
  1000000001010101
c 位 运算符

评论

0赞 Jabberwocky 6/22/2023
而”空白“将是 0? ”:你还期待什么?
0赞 chqrlie 6/22/2023
@Jabberwocky:在这种情况下,行为是显而易见的,但是如果被定义为?mask1char mask1 = 0x88;
0赞 Jabberwocky 6/22/2023
@chqrlie这不是完全相同的问题,因为我们会有标志扩展问题,但很公平。char ...
1赞 Gerhardh 6/22/2023
“它会将 mask1 从 2 个字节转换为 4 个字节吗?”在计算按位 OR 运算的结果时可能会发生这种情况,但是当该结果被分配给时,只保留 2 个字节,并且上半部分被切碎。res

答:

3赞 Vlad from Moscow 6/22/2023 #1

表达式的两个操作数

mask1|mask2;

转换为类型(或者如果类型无法表示操作数的所有值),因为整数升级保留了操作数中存储的值。intunsigned intint

来自 C 标准(6.5.12 按位(包括 OR 运算符)

3 通常的算术转换是在操作数上执行的。

(6.3.1.8 常用算术转换)

1 许多期望算术类型操作数的运算符会导致 转化和产生结果类型的方式类似。目的是 确定操作数和结果的常见实数类型。

这种模式称为通常的算术转换: 否则(注意:如果两个操作数都不是实数类型 - 由我添加), 整数提升在两个操作数上执行

和 (6.3.1.1 布尔值、字符和整数)

如果 int 可以表示原始类型的所有值(如限制 按宽度,对于位域),该值将转换为 int; 否则,它将转换为无符号整数。这些被称为 整数促销。所有其他类型都由整数保持不变 促销。

因此,例如,存储在内部类型的对象中的值将在该类型的对象中表示,前提是 等于 。存储在该类型的对象中的值将在内部表示,例如0x55unsigned charint0x00000055sizeof( int )40x8055unsigned short0x00008055

在此作业中

res= mask1|mask2;

结果将转换回类型 。unsigned short

评论

3赞 chqrlie 6/22/2023
从技术上讲,如果 type 大于 type,则整数提升会导致两个值都转换为 ,否则两个值都转换为 ,但在这种特殊情况下,结果是相同的。intintshortunsigned int
3赞 Lundin 6/22/2023 #2

这个答案将假设一个具有 8 位字符类型、16 位和 32 位的主流系统(现实世界中所有主流的 32 位和 64 位苦味都是这种情况)。shortint

首先查看隐式类型升级规则。在这种特殊情况下如何工作:

C 语言中的每个操作数都有自己的小行,说明如何处理隐式升级。如果是 ,我们可以看一下 C17 6.5.12 “按位包含 OR 运算符”:|

约束

每个操作数都应具有整数类型。

语义
通常的算术转换是在操作数上执行的。

正如我们从本文顶部的链接中了解到的那样,整数升级通常算术转换的一部分。所以在表达式中,两个操作数都是小整数类型,因此被提升为有符号的操作数。这有点不幸,因为我们希望避免使用像瘟疫这样的有符号操作数进行按位算术,尽管在这种特定情况下它没有区别。我们将得到0x00008055和0x00000055 0x55而不是0x8055和 - 基本上只是零填充。res = mask1 | mask2;int

因此,它是 100% 等价的,并且结果是 的类型。res = (int)mask1 | (int)mask2;mask1 | mask2int


接下来,它存储在 .然后发生的是“分配期间的转换”,6.5.16:resunsigned short

简单赋值 () 中,右操作数的值被转换为 赋值表达式,并替换存储在由左操作数指定的对象中的值。=

C17 6.3.1.3 中提供了此转换所需的具体规则:

否则,如果新类型为无符号,则通过重复添加或减去比新类型中可以表示的最大值多一个来转换该值,直到该值在新类型的范围内

这种转换的工作方式类似于模数,或者如果您要对原始值进行二进制截断,则简单地丢弃最高有效字节。

在这种特定情况下,我们有一个 with 值,它被转换为 with value 。int0x00008055unsigned short0x8055


关于“分配期间的转换”的一个奇怪的说明是,所有这些行也都发生了这种情况:

unsigned char mask1 = 0x55; //01010101
unsigned short int mask2 = 0x8055;//1000000001010101   
unsigned short int res = 0;

这里的数字等,在形式上称为整数常量。C 中的整数常量具有根据各种复杂规则(C17 6.4.4.1)选择的类型 - 我不会在这里提及它们,但现在我们可以注意到整数常量的类型永远不会小于 。因此,在上述所有初始化过程中,我们都有从左操作数类型到类型的隐式转换。0x55intint

0赞 chux - Reinstate Monica 6/23/2023 #3

会发生什么..比较和...?unsigned short intunsigned char

OP大多有它。

通常的促销活动

运算符 like 和 else 首先提升每个比 to 更窄且不变的对象。如果包含窄类型范围,则对象变为 ,否则变为 .|, ^, &, + , -int/unsignedint/unsignedintintunsigned

在 OP 的情况下,可能提升到(或者可能是 16 位)。当然会变成一个.unsigned short intintunsignedunsignedunsigned charint

转换为普通类型

然后,排名较低的对象将转换为与排名较高的对象相同的对象。这可能涉及将负数转换为负数或将某些整数转换为浮点数的值更改。intunsigned

在 OP 的情况下,2 个操作数是 then(或者可能是 if 是 16 位)。使用 OP 的值时,此步骤中不会发生值更改。intunsignedunsigned

运算符 | applied

mask1 | mask2然后几乎按照 OP 假设的 2 秒执行。int

  0b00000000`00000000`00000000`01010101
| 0b00000000`00000000`10000000`01010101
  -------------------------------------
  0b00000000`00000000`10000000`01010101

或将 16 位作为 2 .int/unsignedunsigned

  0b00000000`01010101
| 0b10000000`01010101
  -------------------
  0b10000000`01010101

赋值缩小了类型范围

然后将 (或 ) 结果转换为 然后进行赋值。intunsignedunsigned short

当该值可在新的窄类型中表示时,即保存的值。否则,如果目标类型是有符号整数,则该值将以实现定义的方式进行转换。最常见的实现定义方式只是使用最低有效位。否则,如果目标类型是无符号整数,则使用最低有效位(即“mod”(最大值 + 1))。

在 OP 的情况下,结果在 和 的范围内产生一个值,因此被保存。mask1 | mask2unsigned short0b1000000001010101