【问题标题】:What is the use of unsigned char in bitwise negation operation?unsigned char 在按位求反运算中有什么用?
【发布时间】:2018-01-19 12:43:30
【问题描述】:
unsigned char a = 10,b;
b = ~a;
printf(" %d \n ",b);

输出:

245

如果我使用 int 而不是 unsigned char ,则相同的程序将 o/p 更改为 -11

【问题讨论】:

    标签: c unsigned-char negate


    【解决方案1】:

    在 C 中,int 和 char 必须是类型

    1. 已签名
    2. 未签名

    如果用户给出 int [16 位] 或 char [8 位],则编译器默认采用带符号的。对于未签名的,用户需要明确提及。

    unsigned char 范围从 0 到 255 [8 位]。

    signed char 范围从 -128 到 0 到 127 [8 位]。

    根据您的程序,

    案例一:

    a 是无符号字符,输出 245 的范围是 0 到 255。所以你得到的输出是 245。

        0, 1, 2, 3, 4, ..................... 254, 255
        |                                          |
        |<- <- <- <- <- <- <- <- <- <- <- <- <- <- |  
    
        example:
              unsigned char a = 257;       // more than 255.
              printf("a = %d", a);          
    
             **Output** : a = 1.        // [ 0,1,...255,0 [256th], **1** [257th]
    
             unsigned char a = 129;       // with in range of 255.
              printf("a = %d", a);          
    
             **Output** : a = 129.                   
    

    案例2:

    a 是有符号字符,相同的输出 245 从 0 到 127 开始,然后从 -128、-127、-126、..... -11(第 245 位)开始并打印 -11。

     -128, -127, -126, ..... -11, -10, .. -1, 0, 1, 2, 3, .....126, 127
       |                                                             |       
       |<- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <-<-|
    
     example:
              char a = 257;          // more than 255.
              printf("a = %d", a);          
    
             **Output** : a = 1.     // [ 0,1,...127,-128,-127,....-1, 0 [256th], **1** [257th].
    
              char a = 129;              // with in range of 255.
              printf("a = %d", a);          
    
             **Output** : a = -127.      // [ 0,1,...127,-128 [128th], **-127** [129th].             
    

    同样的情况也适用于 int 数据类型。

    【讨论】:

      【解决方案2】:

      变量a 包含unsigned char 值10。假设正统设置具有8 位char 和32 位int 类型,那么a 中的位是:

      0000 1010
      

      b 中存储的值是a 中的按位倒数,这意味着它包含以下位,对应于十进制的 245:

      1111 0101
      

      unsigned char 传递给printf()

      printf("a = %d\n", a);
      printf("b = %d\n", b);
      

      int 短的类型(字符类型和short 类型——我假设short 是16 位类型,通常但不一定是这种情况)会自动提升为@987654337 @。由于源类型为 unsigned char,因此该值被转换为正值 int 值 — 示例代码中为 10 和 245。

      将类型从 unsigned char 更改为 int,然后事情就会改变。

      a:  0000 0000 0000 0000 0000 0000 0000 1010
      b:  1111 1111 1111 1111 1111 1111 1111 0101
      

      由于2's complement arithmetic(存储)的工作方式,b 中的位模式对应于-11。而当ab 的这些版本传递给printf() 时,不需要进行转换,所以printf()1010-11 视为值,并相应地打印。

      重点是推广操作。

      在 C 标准中,它说:

      6.5.2.2 函数调用

      ¶7 如果表示被调用函数的表达式具有包含原型的类型, 参数被隐式转换,就像通过赋值一样,转换为 对应的参数,取每个参数的类型为不合格版本 其声明的类型。函数原型声明器中的省略号表示 参数类型转换在最后一个声明的参数之后停止。默认参数 提升是在尾随参数上执行的。

      当然printf()的声明类型是:

      int printf(const char * restrict format, ...);
      

      因此,ab 的值位于最后声明的参数之后,并受“默认参数提升”的约束。它们在第 6 段中(部分)指定:

      ¶6 如果表示被调用函数的表达式的类型不包含 原型,整数提升对每个参数执行,参数 将类型 float 提升为 double。这些被称为 default 参数 促销。 …

      你必须再往前走一点才能找到整数促销

      6.3.1 算术操作数

      6.3.1.1 布尔值、字符和整数

      ¶2 可以在表达式中使用 int 或 unsigned int 的任何位置 使用:

      • 具有整数类型的对象或表达式(intunsigned int 除外) 其整数转换等级小于或等于int 的等级并且 unsigned int
      • _Boolintsigned intunsigned int 类型的位字段。

      如果int 可以表示原始类型的所有值(受宽度限制,对于 位域),该值被转换为int;否则,它将转换为unsigned int。这些被称为整数提升58) 整数促销。

      ¶3 整数提升保留值,包括符号。如前所述,无论是 ‘‘plain’’ char 被视为已签名是实现定义的。

      等级的概念在 §6.3.1.1 ¶1 中有定义,我不会引用它(它相当长且非常详细)。

      关键是较小的类型(如char)被提升为int,并保留包括符号在内的值。而你所看到的正是这一点。


      测试代码

      #include <stdio.h>
      
      int main(void)
      {
          unsigned char a1 = 10;
          unsigned char b1 = ~a1;
          printf("a1 = %d, b1 = %d\n", a1, b1);
      
          int a2 = 10;
          int b2 = ~a2;
          printf("a2 = %d, b2 = %d\n", a2, b2);
      
          return 0;
      }
      

      输出:

      a1 = 10, b1 = 245
      a2 = 10, b2 = -11
      

      【讨论】:

      • 重要的是由~ 本身引起的整数提升,因为这会导致符号的无声变化。这发生在 printf 默认参数提升之前。
      • @FilipKočica:我不确定你做了什么来打印 245。我在答案末尾添加了一个“测试代码”部分,显示了我得到的结果(在运行 macOS Sierra 10.12.6 和 GCC 7.1.0 的 Mac 上,尽管您尝试使用的大多数机器无论如何都会产生相同的结果) .
      • 啊,我当然以为你在解释从8b32bcharint 的提升。我的错。
      【解决方案3】:

      无符号字符

      a

      |128|64|32|16|8|4|2|1|
      |0  |0 |0 |0 |1|0|1|0|
      

      等于10

      ~是位否定

      (unsigned char)~a

      |128|64|32|16|8|4|2|1|
      |1  |1 |1 |1 |0|1|0|1|
      

      等于245

      由于8b char 小于32b int,该值被扩展为32b,但在调用printf() 时符号被保留。

      内部

      Two's complementusually 用于签名操作。让我们期望sizeof(int) 等于4B32bit's)。 (int 的大小取决于架构。)

      10 在 4B

      |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |1|0|1|0|
      

      ~10 在 4B

      |1|1|1|1| |1|1|1|1| |1|1|1|1| |1|1|1|1| |1|1|1|1| |1|1|1|1| |1|1|1|1| |0|1|0|1|
      

      decimal中获取价值

      • ~否定值
      • 加1
      • 更改标志

      所以你有

      - |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |1|0|1|1|
      

      它等于十进制的-11

      unsigned char 在按位求反运算中有什么用?

      通过使用unsigned char,你得到了8位的unsigned,这意味着值0..255(2^8 -1)

      所以对于signed char,你有-127..127 的值。 (+/-)2^7 -1

      无符号字符仅表示:在执行算术运算时,使用最高有效位而不是将其视为 +/- 符号的位标志。

      【讨论】:

      • 您需要解释的是 ~ 实际上根本不适用于 unsigned char 类型,而是适用于 int 类型,因为 整数提升,这是这个问题的核心。这意味着代码if(~a &lt; 0) puts("oops") 将打印“oops”。但是,如果操作写成 int b = (unsigned char)~a; 之类的东西,结果将保持 245。
      【解决方案4】:

      每当~ 用于小整数类型 的操作数时,例如charunsigned charshort 等,该操作数总是隐式提升为int 类型.这被称为整数提升规则

      这意味着无论您的示例中的a 是什么字符类型,它都会被提升为int,这是一个有符号类型。表达式~a 等价于~(int)10。结果将是(假设是 32 位 CPU)0xFFFFFFF5,用二进制补码表示为 -11

      但是,当您将此数字存储回无符号字符时,它将被转换为值为245 的无符号类型。当您将b 声明为int 时,不会发生这种转换并且值仍然是-11

      以上就是为什么~ 是一个相当危险的运算符使用的原因。在更复杂的表达式中,它可能会产生由符号的无声变化引起的非常微妙的错误。

      【讨论】:

        【解决方案5】:

        int 变量存储32 bits,第一位代表数字的符号。 如果该位打开,则该数字为负数。

        负数表示如下:

        -1 | 11111...111
        -2 | 11111...110
        -3 | 11111...101
        -4 | 11111...100
        

        序列几乎与正整数相同,但以reversed 的顺序排列。

        【讨论】:

        • 根据标准,int 可以窄至 16 位,也可以宽于 32 位。
        • 是的,大卫鲍林,你是对的。但是我从来没有遇到过具有这种标准的 pc(或编译器)。我认为大多数 PC 都是 int - 32 位,long long - 64 位。
        • 注意:2017 年生产的大多数处理器不适用于 PC。每年有数十亿美元用于其他设备。 e.g. 至少有数百万是 16 位设备。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-03
        • 2020-10-08
        • 2016-07-07
        • 1970-01-01
        • 2013-01-07
        • 2015-11-23
        相关资源
        最近更新 更多