【问题标题】:sprintf to convert hexadecimal array to decimal char array only reads first bytesprintf 将十六进制数组转换为十进制字符数组只读取第一个字节
【发布时间】:2018-09-08 21:25:09
【问题描述】:

我有一个数组:

unsigned char datalog[4];
datalog[0] = 0;
datalog[1] = 0xce;
datalog[2] = 0x50;
datalog[3] = 0xa3;

这些代表十六进制值 0xce50a3。它的十进制值为13521059。

我需要将此十六进制值转换为十进制数组,最好使用 sprintf,这样最终结果将是:

finalarray[0] = '1';
finalarray[1] = '3';
finalarray[2] = '5';
finalarray[3] = '2';
finalarray[4] = '1';
finalarray[5] = '0';
finalarray[6] = '5';
finalarray[7] = '9';

我尝试了几种 sprintf 输入组合,包括将我的十六进制数组连接成 unsigned long datalogvalue = 0xce50a3。但是 sprintf 只在转换时读取它的第一个字节。

例如:

sprintf(finalarray, "%d", *(unsigned long *)datalog);

产量:

finalarray[0] = '2';
finalarray[1] = '0';
finalarray[2] = '6';
finalarray[3] = ' ';
.....

206 是 0xce 的十进制表示。所以它只转换第一个十六进制字节而不是其余的。

关于如何将整个 unsigned long 转换为十进制数组有什么想法吗?

【问题讨论】:

标签: c arrays hex printf decimal


【解决方案1】:

char* 转换为unsigned long* 并取消引用该指针的结果取决于系统的字节序。除非此特定计算的效率对您的程序性能至关重要,否则不要使用此类技巧。使用简单的逻辑。

int res = (datalog[0] << 24) +
          (datalog[1] << 16) +
          (datalog[2] << 8) +
          datalog[3];

sprintf(finalarray, "%d", res);

如果您需要为您的类型使用unsigned long,请确保在对sprintf 的调用中为unsigned long 使用正确的格式说明符。

unsigned long res = (datalog[0] << 24) +
                    (datalog[1] << 16) +
                    (datalog[2] << 8) +
                    datalog[3];

sprintf(finalarray, "%lu", res);

【讨论】:

  • 为了扩展您的第一段,由于破坏了严格的别名,这样做实际上是未定义的行为,因此永远不应该这样做。 (不过,可以将 unsigned long* 转换为 char * 并取消引用它,因为 char-family 类型有一个例外,可以为任何东西取别名。
  • datalog[0] &lt;&lt; 24 取决于 int/unsigned 的大小为 32 位或更多。 res 的类型不适用于班次。代码可能应该是 "%lu" 而不是 "%ul"
  • @chux,感谢您提出大小问题并指出格式字符串错误。
【解决方案2】:

一个不依赖字节序、int 大小或指针技巧的简单解决方案

形成价值

//              LU to use unsigned long math
((datalog[0]*256LU + datalog[1])*256 + datalog[2])*256 + datalog[3]

打印出来

sprintf(finalarray, "%lu", value);

一共

sprintf(finalarray, "%lu", 
    ((datalog[0]*256LU + datalog[1])*256 + datalog[2])*256 + datalog[3]);

【讨论】:

    【解决方案3】:

    由于字节序,字节不会按照您认为的顺序出现:

    IDEOne Link

    #include <stdio.h>
    
    int main(void) {
    unsigned char datalog[4];
    char finalarray[20] = {0};
    
    datalog[0] = 0xa3;
    datalog[1] = 0x50;
    datalog[2] = 0xce;
    datalog[3] = 0x00;
    
    sprintf(finalarray, "%lu", *(unsigned long*)datalog);
    
    printf("Answer: %s\n", finalarray);
    return 0;
    }
    

    输出

    Success #stdin #stdout 0s 4180KB
    Answer: 13521059
    

    【讨论】:

    • *(unsigned long*)datalog 肯定无法满足对齐要求
    【解决方案4】:

    首先,字节序在这里让事情变得有点麻烦。 为了能够将您的缓冲区重新解释为 32 位 int,您必须在打包时考虑字节序。

    例如,在我的小端系统上,如果转换为 32 位无符号整数,数据日志将被解释为:2739981824。

    因此,我必须根据以下示例中的 datalog2 打包我的数据,以获得所需的 13521059。

    #include <stdlib.h>
    #include <stdio.h>
    #include <stdint.h>
    
    int main() {
    
        uint8_t datalog[4];
        datalog[0] = 0;
        datalog[1] = 0xce;
        datalog[2] = 0x50;
        datalog[3] = 0xa3;
    
        uint32_t temp = *((uint32_t*) datalog);
    
        printf("%u\n", temp); // 2739981824
    
    
        uint8_t datalog2[4];
        datalog2[0] = 0xa3;
        datalog2[1] = 0x50;
        datalog2[2] = 0xce;
        datalog2[3] = 0;
    
        uint32_t temp2 = *((uint32_t*) datalog2);
    
        printf("%u\n", temp2); // 13521059
    
        return 0;
    }
    

    但是,您所问的还有另一个问题。

    如果我正确地解释了您的问题,您希望得到另一个数组,其中每个小数在 base-10 中构成 13521059,并以自己的索引结束。

    为了做到这一点,您必须能够使用每个索引来处理 log2(10) 位,这是不可能的。

    因此,为了获得您建议的包装数组,您必须手动转换它。

    【讨论】:

      【解决方案5】:

      正如其他人所提到的,尝试按数字顺序读取数组的字节将取决于系统,因为 Big Endian 和 Little Endian 系统会给出不同的结果。

      此外,通过指针技巧进行类型双关语是未定义的行为,因为它破坏了严格的别名。将双关语键入除字符族数组以外的类型的合法方法涉及使用联合以多种方式表示数据。但是,由于上述 Endian 问题,您不应针对此问题执行此操作,而应执行 R Sahu 的回答中提到的位移方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-06-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-31
        • 2019-02-12
        相关资源
        最近更新 更多