在C语言中将十六进制转换为二进制

4

我目前在维护一段代码,这段代码是我从之前的开发人员那里继承下来的。我需要理解一个将十六进制 ID 转换为二进制的函数。我无法完全理解他背后的逻辑。以下是代码:

int iHex2Bin(char * pchIn, char * pchOut, int iLen)
{
int i;

memset(pchOut, 0, iLen);

for(i=0; i<iLen; i++)
{
    pchOut[i] = (((unsigned char) *pchIn)-0x30)*16;
    pchIn++;
    pchOut[i] += (((unsigned char) *pchIn)-0x30);

    pchIn++;
}
return(0);
}

根据名称,它将十六进制转换为二进制。我不理解函数的逻辑。能否有人解释一下?谢谢。

感谢你们的回答。现在有点清楚了。我还添加了相反的功能以进行更多分类:

int iBin2Hex(char * pchIn, char * pchOut, int iLen) { int i;

unsigned char chInBuff[256]={0};
char chOutBuff[513]={0};
char chTemp[3]={0};

if(iLen>256)
    return(-2);

memcpy(chInBuff, pchIn, iLen);
for(i=0; i<iLen; i++)
{
    sprintf(chTemp, "%02X", chInBuff[i]);
    strcat(chOutBuff, chTemp);
}

memcpy(pchOut, chOutBuff, iLen*2 + 1);
return(SUCCESS);

我认为这些函数是必要的,但函数名称非常令人困惑。可以使用C库函数。但代码正在正确运行,所以我没有做任何更改。只需要在将来需要时理解代码即可。


8
在C语言中,所有的数字都是二进制的,因此这段代码实际上是将一个十六进制ASCII字符串转换为二进制。其中0x30是表示字符'0'的ASCII码。但是,这段代码无法支持A到F之间的字母。解决方案是放弃这个代码,并使用strtol(str, NULL, 16)代替。 - Lundin
@Lundin “扔掉这个垃圾”似乎有点过早,因为不知道它的使用情况。可能存在对看起来不正确的遗留行为的依赖关系,并且重写函数以执行它应该执行的操作可能会严重破坏其他东西。是的,它看起来像垃圾,但它产生的垃圾可能是一种预期的形式。在查看它的使用方式之前,我会非常小心地更改此函数。完全。 - Andrew Henle
1
它可能是反转BCD或简单的基于16进制编码(使用16个字符0123456789:;<=>?)。没有理由认为它是不正确的(除了名称有些误导,特别是如果它是BCD)。 - Dipstick
3个回答

3
我认为这个函数并不完全按照你所说的那样执行。它以一个字符串作为输入,并生成另一个字符串作为输出,但我不会将输入标记为“十六进制”。输出是一系列带有二进制值的有符号字节序列。
让我们看看代码的核心:(((unsigned char) *pchIn)-0x30)表达式。这读取单个字符并减去0x30,这是'0'的ASCII值。因此,此表达式正确地将ASCII数字转换为0到9之间的值。它不能正确处理任何其他字符。具体而言,它将不会为字符'A''F''a''f'产生正确的值10-15。输入字符串中替代字符乘以16的事实...不够使其成为十六进制。
基本上,它正在接受一个(假定长度为偶数)的字节串A,B,C,D,... 并将其转换为一个字节串A*16+B,C*16+D,...
您最好使用标准库函数之一将字符串转换为实际的二进制值(例如,一个int),这很容易检查、打印等等...然后将该二进制值转换为另一种格式的字符串。请参阅任何手册或在线文档。我相信strtolltoa将满足您的需求。

这是因为“二进制”一词的歧义性。有些人将其视为零和一的字符串,而其他人则指8位数据八位组,而不是十六进制字符串。它没有确切的定义。 - Ctx

2
如果函数内部的代码正确,则该函数被命名错误。它看起来很业余,但可以想象它旨在读取ASCII数字序列(无论C实现的执行字符集是什么),并写入二进制编码的十进制数。二进制编码的十进制数

例如,这个例程可以用于读取ASCII文本并为使用二进制编码的十进制数的金融软件做准备。(不使用ASCII的系统现在很少见,使用二进制编码的十进制数的系统或应用程序也很少见,但代码显示出这两者的迹象是意图而非意外的一些证据。)
有了这个想法,我们可以解释代码:
for(i=0; i<iLen; i++)

这个迭代过程针对每一组输出数字执行一次。因此,处理的数字数量为2*iLen,写入的字节数量为iLen

    pchOut[i] = (((unsigned char) *pchIn)-0x30)*16;

这个操作从输入中获取一个字符并减去ASCII码中“0”的代码。由于ASCII数字“0”到“9”具有连续的代码,结果是由数字0到9表示的数字。然后将此数字乘以16,相当于左移,并存储在当前输出字节中。

    pchOut[i] += (((unsigned char) *pchIn)-0x30);

这个计算将由下一个输入字符表示的数字加到当前输出字节中。结果是两个数字被打包在一个字节中,每个数字编码为四位。例如,十进制数“74”在输入时将被编码为7416 (0x74),这是11610


0
除了其他人说的错误之外,代码可以使用以下内容将连续的十六进制字符对转换为一个字节。
// inside the loop
pchOut[i] = strtoul((char[3]){pchIn[0], pchIn[1]}, 0,16);
pchIn += 2;

这里使用了复合字面量,自C99以来就可用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接