我正在学习C,我无法弄清楚其中一个K& R练习,列表:
练习2-3,编写功能
htoi(s)
,用于转换字符串 十六进制数字(包括 等效的0x
或0X
} 整数值。允许的数字 是0
到9
,a
到f
和A
通过F
。
我想我需要在这里做一些递归,我猜我不太了解编号类型及其各种转换方法等。
有人可以给我一些关于如何最好地理解它的指示,我不是想找人握住我的手,而是指导我找到正确理解的方法,所以我可以用最优雅的形式写出来,而不是printf("%x", skill);
答案 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