十六进制到十进制转换[K& R练习]

时间:2009-04-25 01:23:39

标签: c kernighan-and-ritchie

我正在学习C,我无法弄清楚其中一个K& R练习,列表:

  

练习2-3,编写功能   htoi(s),用于转换字符串   十六进制数字(包括   等效的0x0X}   整数值。允许的数字   是09afA   通过F

我想我需要在这里做一些递归,我猜我不太了解编号类型及其各种转换方法等。

有人可以给我一些关于如何最好地理解它的指示,我不是想找人握住我的手,而是指导我找到正确理解的方法,所以我可以用最优雅的形式写出来,而不是printf("%x", skill);

8 个答案:

答案 0 :(得分:12)

没有必要进行递归。您只需要在字符串上向后循环(即从单位列开始),将单位数转换时间相加为基数位置乘数。这是伪代码,不处理可选的0x前缀(并且不检查溢出的可能性):

long total = 0;
long multiplier = 1;
for (int i = string.length - 1; i >= 0 i--)
{
   digit = ConvertSingleHexDigittoInt(string[i]);
   total += digit * multiplier;
   multiplier *= 16;
}

我已经将ConvertSingleHexDigittoInt()的简单实现留给了你:)

答案 1 :(得分:5)

米奇有正确的基本想法,但让我们更详细一点。

十六进制数字只是基数16,这意味着数字(从右到左)的值为

数字×16 0 (即1)
数字×16 1 (即16)
数字×16 2 (256)

等等。因此,例如,0xE为14。

你想要的是从字符串的结束开始的循环。假设字符串是s,length(s)是字符串的长度。在伪代码中,你想要

value = 0
r = 1   // ask yourself "what values does r take as this proceeds?"
for i from length(s)-1 to 0   // Ask yourself "why length(s)-1?"
   value = value + (digitval(s[i])*r)
   // get ready for the next digit
   r = r * 16

digitval(char c)需要是一个函数,将“0123456789ABCDEF”中的checract转换为0到15之间的值(包括在内)。我将把它留作练习,只有一个提示:“数组”。< / p> 小心一个额外的问题;因为你可能有一个前导“0”或“0x”,你需要确保你处理这些情况。

答案 2 :(得分:4)

对于那些对数学感到满意的人来说,从左到右处理字符串更简单,可以说更具可读性。该战略正在意识到,例如,1234 = (((1 x 10) + 2) x 10 + 3) x 10 + 4

换句话说,当您从左到右处理每个数字时,将前一个数字乘以基数,有效地“向左移动”一个位置,然后添加新数字。

long decFromHexStr(const char *hexStr)
{
    int i;
    long decResult = 0;  // Decimal result

    for (i=0;  i < strlen(hexStr);  ++i)
    {
        decResult = 16 * decResult + decFromHexChar(hexStr[i]);
    }
    return decResult;
}

经验丰富的程序员可能会使用指针来逐步执行字符串,而不是将其视为数组:

long decFromHexStr(const char *pHex)
{
    long decResult = 0;

    while (*pHex != '\0')
    {
        decResult = 16 * decResult + decFromHexChar(*pHex++);
    }
    return decResult;
}

自从你学习以来,值得研究编码风格并决定你是否觉得它有用,这样你就能早日养成良好的习惯。

玩得开心!

答案 3 :(得分:2)

十六进制数实际上是什么意思?我们采取 15FA 。这意味着

1 * 16^3 + 5 * 16^2 + 15 * 16^1 + 10 * 16^0

请注意, A 代表十, B 十一,依此类推,直至 F ,代表十五。 16 ^ 0也等于1.

所以我们需要做的就是计算上面表达式的值!最简单的方法可能是按此顺序执行:

10 * 1
15 * 16
5  * 256   //256  = 16 * 16
1  * 4096  //4096 = 16 * 16 * 16

如果有更多数字,这可以继续。你真正需要的只是一个循环和几个变量。

还有另一种方法可以解释上述表达式:

((1 * 16 + 5) * 16 + 15) * 16 + 10

如果您愿意,请尝试以下每种方法。

更高级的信息:

基本上,计算机使用基数2(也称为二进制)来表示所有数字和计算。甚至字符串“1A6DC0”也用1和0编码,最终在屏幕上显示为字母和数字。

有时你可以利用计算机使用二进制文件这一事实,但通常你不需要考虑它。

例如,当你做

x = (11 + y) * 6;
不需要担心 11和6将在某个阶段表示为一系列高低电压。它只是按你的预期工作。在十进制(我们使用的数字系统)到二进制和后退之间转换是计算机可以轻松完成的一个简单过程,因此他们会自动为我们这样做,以使我们的工作更轻松。

但是,在十六进制和二进制之间进行转换时,有一个快捷方式。由于四个二进制数字与单个十六进制数字相同,因此您只需将每个十六进制数字转换为二进制单独,然后将它们串在一起。

例如, 15FA 会像这样展开:

1 -> 0001
5 -> 0101
F -> 1111
A -> 1010
15FA -> 0001 0101 1111 1010

请注意,这通常不能直接完成,通常涉及逻辑或位移(|<<)。有趣的东西。

答案 4 :(得分:1)

传统方法从左向右转换。累加器在开始时设置为零,并在将每个新数字的等效值添加到循环之前乘以16。

对于期望具有可选前导htoi()的十六进制数字的0x函数,首先跳过这些字符(如果存在)。直接检查s[0]s[1]的值可能是那里最明确的方法。

如果您知道数字是ASCII,那么您可以使用s[i] - '0's[i] - 'A' + 10之类的表达式将第i个数字转换为整数值。

你可能想把整个事情折叠成一个案例以保持理智。

修改:将*s更改为s[i],以便与本练习的观点指出来自未来的观察保持一致。

请注意,还有其他几种方法可以将各个数字转换为值。例如,您可以在所有数字的向量中查找它们(类似strchr("0123456789ABCDEF",s[i])),构建一个由字符代码索引的查找表,每个位置的每个数字的值(digitvalue[s[i]]之后{ {1}}已被适当初始化),对另一个答案中建议的每个可能的数字使用带有int digitvalue[256]标签的switch (s[i])语句,或者使用上面建议的范围检查和算术。需要考虑的是选择哪个,以及为什么。请注意,它可能不是一个明显的选择,如果ASCII不是您选择的字符集,最佳答案可能会有所不同。

答案 5 :(得分:1)

我可能没有做出很大的贡献,上面有很好的答案。但我会试一试。

正如其他人在我之前所做的那样,我将留下一些功能供您实施。

int htoi(const char* x)
{

        unsigned int current_position;/*current position is to be defined*/
        int prefixed=0;                                                         
        int dec=0;
        char* y = x;

        if (x && x+1 && (*(x+1)=='x' || *(x+1)=='X')){  /*Is 0x or 0X prefix present?*/
                prefixed= PREFIXED;             
        }

        if (prefixed) y+=2; /*Jumps over 0x or 0X*/     


        while (*y){
                /*getPos(const char*) and singleHexToDec(const char*,unsigned int) functions to be implemented*/
                current_position=getPos(y);
                dec+=singleHexToDec(y,current_position); 
        }
        return dec;
}

答案 6 :(得分:1)

尝试用粗鲁的英语解释:(

我的代码(假设所有输入都是纠正的。避免防御性编程)

#include <stdio.h>


enum { SZ = 11 };

unsigned int htoi(const char *s);


int main()
{

  char buff[SZ];  //Max 11 char: 0x XX XX XX XX '\0' (2 + 8 + 1)

  while(fscanf(stdin, "%s", buff) != EOF)
    printf("%X\n", htoi(buff) ); 

  return 0;
}


unsigned int htoi(const char *s)
{
  unsigned int i, r = 0;

  for(i = (s[1] == 'x') ? 2 : 0; s[i] != '\0'; i++)
    r = ( r << 4 ) +  ( (s[i] > '9') ? 0x9 : 0x0 ) + ( s[i] & 0xF );

  return r;
}

好的,首先,指定r = 0。 然后,当我们开始for-bucle时,我们给索引变量i赋予一个init值。我们必须检查字符串是否有0x格式。我们只需要检查位置1以了解我们是否正在处理0x格式的输入字符串或没有它。

现在,我们有一个指向第一个正确字符的索引!对于每个迭代,我们向左移位4位。我们获得4个零。添加新十六进制数字的完美差距!例如:

Input: 0xBE1234

Is s[1] == 'x' ? true then i = 2;
r = 0;

iter 1: r = 0x0; r = 0x0; r = 0xB;
iter 2: r = 0xB; r = 0xB0; r = 0xBE;
iter 3: r = 0xBE; r = 0xBE0; r = 0xBE1;
iter 4: r = 0xBE1; r = 0xBE10; r = 0xBE12;
iter 5: r = 0xBE12; r = 0xBE120; r = 0xBE123;
iter 6: r = 0xBE123; r = 0xBE1230; r = 0xBE1234

可能这有点复杂:

 r = ( r << 4 ) + ( (s[i] > '9') ? 0x9 : 0x0 ) + ( s[i] & 0xF );

首先,我们取代4位,与每16位乘法相同,但效率更高。然后,我们看看是否有一个大于'9'的ASCII字符。如果这是真的,我们正在使用A,B,C,D,E,F或a,b,c,d,e,f。请记住,我们假设我们有正确的输入。 好的,现在看看ASCII表:

A = 0100 0001  -  a = 0110 0001
...
F = 0100 0110  -  f = 0110 0110

但我们想要这样的事情:

A = 0000 1010  -  a = 0000 1010
...
F = 0000 1111  -  f = 0000 1111

我们是怎么做到的?在位移之后,我们用掩模s [i]&amp;清除4个最重要的位。 0xF:

s[2] == 'B' == 0100 0010
s[2] & 0xF == 0000 0010

并添加9以适应整数值(仅在{'A'...'F','a'...'f'}中的s [i]的情况下

s[2] & 0xF + 0x9 = 0000 0010 + 0000 1001 = 0000 1011 (0xB)

最后,我们添加移位的r值并分配给r。第二次迭代的执行顺序(s [3]):

r == 0xB, s[3] == 'E' == 0100 0101 (start iter 2)
(r << 4) == 0xB0, s[3] == 'E' == 0100 0101 (displacement r << 4 )
(r << 4) == 0xB0, (s[3] & 0xF + 0x9) == 0000 1110 == 0xE (clear most significant bits of s[3] and add 0x9)
r = (r << 4) + ( s[3] & 0xF + 0x9 ) == 0xBE == 1011 1110 (add all and assign to r)

如果我们有一个像s [4]这样的数字字符会怎么样?

s[4] == '1' == 0011 0001
s[4] & 0xF == 0000 0001

位移r四个位置,加0(无),添加逻辑运算结果s [i]&amp; 0xF,最后,分配给r。

r == 0xBE, s[4] == '1' == 0011 0001 (start iter 3)
(r << 4) == 0xBE0, s[4] == '1' == 0011 0001 (displacement r << 4 )
(r << 4) == 0xBE0, (s[4] & 0xF + 0x0) == 0000 0001 (clear most significant bits of s[4] and add 0)
r = (r << 4) + s[4] & 0xF == 0xBE1 == 1011 1110 0001 (add all and assign)

请记住,我们移位4因此我们不对数字位进行网格划分,因为我们正在添加不太重要的位,间隙为四个零。

PD:我保证提高英语水平,以便更好地解释,抱歉。

答案 7 :(得分:-2)

昨天我写了这样一个函数。你可以在下面看到我的代码。

/* Converting a hex string to integer, assuming the heading 
   0x or 0X has already been removed and pch is not NULL */
int hex_str_to_int(const char* pch) {

    int value = 0;
    int digit = 0;

    for (; *pch; ++pch) {

        if (*pch >= '0' && *pch <= '9') {
            digit = (*pch - '0');
        } else if (*pch >= 'A' && *pch <= 'F') {
            digit = (*pch - 'A' + 10);
        } else if (*pch >= 'a' && *pch <= 'f') {
            digit = (*pch - 'a' + 10);
        } else {
            break;
        }

        // Check for integer overflow
        if ((value *= 16) < 0 || (value += digit) < 0) {
            return INT_MAX;
        }
    }

    return value;
}

以下是测试代码:

int main(void) {

    printf("%d %d\n", hex_str_to_int("0"), 0x0);
    printf("%d %d\n", hex_str_to_int("A"), 0xA);
    printf("%d %d\n", hex_str_to_int("10"), 0x10);
    printf("%d %d\n", hex_str_to_int("A1"), 0xA1);
    printf("%d %d\n", hex_str_to_int("AB"), 0xAB);
    printf("%d %d\n", hex_str_to_int("100"), 0x100);
    printf("%d %d\n", hex_str_to_int("1A2"), 0x1A2);
    printf("%d %d\n", hex_str_to_int("10A"), 0x10A);
    printf("%d %d\n", hex_str_to_int("7FFFFFF"), 0x7FFFFFF);
    printf("%d %d\n", hex_str_to_int("7FFFFFF1"), 0x7FFFFFF1);
    printf("%d %d\n", hex_str_to_int("7FFFFFF2"), 0x7FFFFFF2);
    printf("%d %d\n", hex_str_to_int("7FFFFFFE"), 0x7FFFFFFE);
    printf("%d %d\n", hex_str_to_int("7FFFFFFF"), 0x7FFFFFFF);
    printf("%d %d\n", hex_str_to_int("80000000"), 0x7FFFFFFF + 1);
    printf("%d %d\n", hex_str_to_int("80000001"), 0x7FFFFFFF + 2);

    printf("%d %d\n", hex_str_to_int("10AX"), 0x10A);   
    printf("%d %d\n", hex_str_to_int("203!"), 0x203);

    return 0;
}

输出以下值:

0 0
10 10
16 16
161 161
171 171
256 256
418 418
266 266
134217727 134217727
2147483633 2147483633
2147483634 2147483634
2147483646 2147483646
2147483647 2147483647
2147483647 -2147483648
2147483647 -2147483647
266 266
515 515