【问题标题】:Split up two byte char into two single byte chars将两个字节字符拆分为两个单字节字符
【发布时间】:2012-11-09 16:32:15
【问题描述】:

我有一个值为 0xB3 的字符,我需要将其拆分为两个单独的字符。所以 X = 0xB 和 Y = 0x3。我试过以下代码:

int main ()
{
    char addr = 0xB3;
    char *p = &addr;

    printf ("%c, %c\n", p[0], p[1]);          //This prints ?, Y
    printf ("%X, %X\n", p[0], p[1]);          //This prints FFFFFFB3, 59

    return 0;
}

为了澄清,我需要将值 00 的任何 2 字节字符转换为 FF,并将第一个和第二个字节分成单独的字符。谢谢。

【问题讨论】:

  • 你有 4 位字符吗?
  • 是的,我建议您了解诸如“字节”之类的常用术语的含义 - 它不是您的工作所必需的,但在与社区互动时很有帮助
  • 仅供参考。 1 或 0 是一个位,一个半字节中有 4 位,一个字节中有 2 个半字节(8 位)。 0xB3 是单个字节,10110011 是位。所以你希望上半字节与下半字节分开。

标签: c char


【解决方案1】:

直接来自Wikipedia

#define HI_NIBBLE(b) (((b) >> 4) & 0x0F)
#define LO_NIBBLE(b) ((b) & 0x0F)

所以HI_NIBBLE(addr) 将是0xB。但是,0x000xFF 不是“双字节”。它们是单字节值。一个十六进制数字可以占用 16 个字节,而一个字节可以占用 256 = 16² 个字节,因此您需要两个十六进制数字来表示任意字节值。

【讨论】:

    【解决方案2】:

    这里有不少问题,我们来看看你的代码:

    int main ()
    {
        char addr = 0xB3;  <-- you're asigning 0xB3 in hex, which is (179 in dec) to addr
        char *p = &addr;   <-- you're assigning a pointer to point to addr
    

    如果 addr 未签名,现在将设置为 179,即 │ ( Box drawing character )extended ASCII

    char 的值可以是 -127 到 +127,如果它是有符号的,或者是 0 到 255,如果它是无符号的。在这里(根据您的输出)它已签名,因此您的分配溢出了 char

    printf ("%c, %c\n", p[0], p[1]); <-- print the char value of what p is pointing to
                                         also, do some UB
    printf ("%X, %X\n", p[0], p[1]); <-- print the hex value of  what p is pointing to
                                         also, do some UB
    

    因此,您的代码的第二部分在这里打印溢出的 addr var 的 char 值,它恰好为您打印 '?'addr 的十六进制值是 FFFFFFB3 表示您有一个负值(最高位是有符号位)。

    这:p[0] 实际上是一个“添加和引用”运算符。意思就是我们要取p的地址,加上0,然后顺从看看结果:

    p ---------+
               V
           ------------------------------------------
          | ptr(0xB3) |    ?      |     ?     | ... |
          -------------------------------------------
           0xbfd56c2b  0xbfd56c2C  0xbfd56c2d   ...      
    

    当您执行 p[1] 时,这将经过一个 char 或一个字节超过 ptr 并为您提供该结果。什么东西在那里?不知道。这超出了您的范围:

    p+1 -------------------+
                           V
           ------------------------------------------
          | ptr(0xB3) |    ?      |     ?     | ... |
          -------------------------------------------
           0xbfd56c2b  0xbfd56c2C  0xbfd56c2d   ...   
    

    Y 的 ASCII 值(十六进制)是 0x59,所以内存中的指针后面是 Y。但它可能是任何东西,将要做什么是未定义的。正确的做法是:

    int main ()
    {
        unsigned char addr = 0xB3;
        char low = addr & 0x0F;
        char high = (addr >> 4) & 0x0F;
    
        printf("%#x becomes %#x and %#x\n", addr, high, low);
    
        return 0;
    }
    

    这通过以下方式工作:

        0xB3  =>  1011 0011        0xB3 >> 4 =   0000 1011
                & 0000 1111                    & 0000 1111
                ------------                  -------------
                  0000 0011 => 3 low             0000 1011 => B high
    

    【讨论】:

      【解决方案3】:

      为什么需要通过指针传递?只需取 4 个相关位并在需要时移动最有意义的位:

      char lower = value & 0x0F;
      char higher = (value >> 4) & 0x0F;
      

      那么0xB3 是一个字节,而不是两个字节。由于一个十六进制数字可以有 16 个值,所以两个数字可以存储 16*16 = 256 个值,即一个字节可以存储多少。

      【讨论】:

        【解决方案4】:

        好的,所以您尝试将 0xB3 拆分为 0xB 和 0x3,仅供参考,不要说“字节字符”,一个字节的 2 个部分通常称为“半字节”,一个字节由 2 个半字节组成(由 4 位组成)。 如果你不知道,char 指的是 1 个字节。

        所以这是你的代码的问题:

        char addr = 0xB3;  <---- Creates single byte with value 0xB3 - Good
        char *p = &addr;   <---- Creates pointer pointing to 0xB3 - Good
        
        printf ("%c, %c\n", p[0], p[1]); <---- p[0], p[1] - Bad
        printf ("%X, %X\n", p[0], p[1]); <---- p[0], p[1] - Bad
        

        好的,所以当您引用 p[0]p[1] 时,您告诉系统指针 p 指向 chars 的数组(p[0] 将引用 0xB3 但 p[1]将转到内存中的下一个字节)

        示例:这是您的系统内存的样子(但使用 8 字节指针)

             Integer Values Area              Pointers Area
        0x01 0x02 0x03 0x04 0x05 0x06    0x12 0x13 0x14 0x15 0x16
        -----------------------------    ------------------------
        .... .... 0xB3 0x59 .... ....    .... .... 0x03 .... ....
        -----------------------------    ------------------------
                   ^    ^                           ^
                  addr  |                           p (example pointer pointing to example address 0x03)
                     Random number                    (Pointers are normally 8 Bytes but)
                     showing up in p[1]               (But In this example I used single bytes)
        

        因此,当您告诉系统获取 p[0]*p 时(它们会做同样的事情) 它将转到地址(例如 0x03)并获取一个字节(因为它是一个字符) 在这种情况下0xB3。 但是,当您尝试p[1]*(p+1) 时,它将转到地址(例如0x03),跳过第一个char 并得到下一个给我们0x59 的地址,这将用于其他一些变量。

        好的,我们已经解决了这个问题,那么您如何获得小食?

        获取半字节的一个问题是通常不能只有半字节的 put 变量,没有类型只支持 4 位。 当您使用 %x/%X 打印时,它只会显示直到最后一个非零数字的半字节,例如。 = 0x00230242 只会显示 230242 但如果你做类似的事情

        %2lX 将显示 2 个完整字节(包括零) %4lX 将显示 4 个完整字节(包括零)

        因此,尝试获取单个小食是毫无意义的,但是如果您想做类似的事情,那就这样做:

        char addr = 0x3B;
        char addr1 = ((addr >> 4) & 0x0F);
        char addr2 = ((addr >> 0) & 0x0F);
        

        【讨论】:

          猜你喜欢
          • 2014-03-30
          • 2011-06-14
          • 2011-10-19
          • 2021-10-08
          • 1970-01-01
          • 1970-01-01
          • 2017-04-15
          • 1970-01-01
          • 2016-04-06
          相关资源
          最近更新 更多