原题出自:《The C Programming Language - Second Edition》一书的练习2-3

编写函数htoi(s),把由十六进制数字组成的字符串(包含可选的前缀0x或0X)转换为与之等价的整型值。字符串中允许包含的数字包括:0~9、a~f以及A~F。


在做这题时,遇到的问题主要是:如何判断字符数组中的这个十六进制数是正数还是负数,如果是负数,应该如何转换?

通过百度了解我发现十六进制的正负是按其对应的二进制的符号位0 or 1决定;

那么非要转换为二进制处理才行吗?怎么转换?又怎么判断哪个是符号位?

带着疑问继续百度,看到百度知道:http://zhidao.baidu.com/link?url=SDoYH-U2h2r2NVo_YOXJS1rK_iYbV5PMuB5ND2EoQEBe5XYmJQdCZ5NCABV06QVhPW2zu8uRfuI5_p2fDTG3-a 

按照答者说的,打开电脑自带计算器看了下:

    windows键 + R --> 输入calc,打开 --> Alt + 3(#),选择程序员型

    玩了一下这个计算器便发现红圈内的东东:

wKioL1XC_FCxWXfrAAHYKh0wAyk345.jpg

也就是说,一个16进制数,如果不知道它是用几位的16进制数来表示,就无法判断符号位。

例如,只给你0xFFFF,判断它是正还是负?

wKiom1XC_J3jiqw7AADsNTXurAw226.jpg

看到这么个评论,我才醒悟。

    答案是无从知晓

如果它是用一个‘字’表示的,则是 -1;

如果它是用双字表示的,则是正数。

通过一段思考,清扫迷雾。现在我的思路是:

   如果给你个字符数组0xF\0或0xFFFFFFFFF\0,C语言中怎么判断它的正负?

    先判断那是几位的十六进制数 /* 这样好判断对应的二进制的符号位 */

    if 长度 < 位数n

         毋庸置疑这是正数;/* 因为在其前补零使其为n位16进制,那二进制符号位必为0 */

    if 长度 = 位数n

         if 第一位 >= 8    /* 为什么是 >= 8 ?; 8(16进) --> 1000(2进)*/

                为负数;

         else 

                为正数;


清除了以上这些迷雾,写代码就不难了:

#include <stdio.h>
#include <math.h>
#include <string.h>

enum inputstat {NO, YES};    /* 输入有误时,normal = NO,不输出错误的num	*/
enum inputstat normal = YES;

int main()
{
	char ox[30];
	long int num;
	long int htoi(char s[]);	/* 如果数组是包含0x或0X的十六进制数,函数将返回对应的整型值;否则输出错误提示	*/ 
	
	gets(ox);
	num = htoi(ox); 
	if (normal == YES)
		printf("%ld\n", num);
	return 0;
}

/* 含一个无小数部分的十六进制数的数组转换为整型值;需要选择16进制数的长度	*/ 
long int htoi(char s[])
{
	int i, j, k, temp, twori, tbit, bit_16;
	long int num;
	/* i为数组s[]当前位置; 
	 * j为16(2)进制的位数减一;
	 * k用于操作时标记每位十六进制数对应的四位二进制数之一; 
	 * temp为0~9和a~f/A~F对应的十进制数;
	 * twori为二进制从左往右的位数;
	 * tbit标记着二进制数最小位;
	 * bit_16表示十六进制数的位数; 
	 */ 
	int two[64];	/* 用于存放二进制的数组,并对其进行操作			*/
	
	printf("请选择十六进制数 %s 的占用内存长度:\n  1、单字节\t\t2、字\n  3、双字\t\t4、四字\n", s);
	bit_16 = pow(2, getchar() - '0');
	if (bit_16 == 1 || bit_16 > 16) {
		printf("error: 选择的长度错误!\n");
		normal = NO;
		return 0;
	}
	j = twori = 0;
	num = 0;
	i = strlen(s);
	if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && i > 2) {	/* 判断s[]是否为以0x或0X开头的数组    */
		if (i - 2 == bit_16 && (s[2] == '8' || s[2] == '9' || s[2] <= 'a' && s[2] >= 'f' || s[i] >= 'A' && s[i] <= 'F')) {			/* 十六进制是否为负数		*/ 
			for (i = 2; s[i] != '\0'; ++i) {		/* 此循环结构将负数转为二进制反码,循环结束后,i为16进制数的位数 + 1(标记着'\0'),twori则标记着二进制数组的最小位 */
				if (s[i] >= '0' && s[i] <= '9')
					temp = s[i] - '0';
				else if (s[i] >= 'a' && s[i] <= 'f')
					temp = s[i] - 'a' + 10;
				else if (s[i] >= 'A' && s[i] <= 'F')
					temp = s[i] - 'A' + 10;
				else {
					printf("error: Contains illegal characters");
					normal = NO;
					return 0;
				}
				for (k = 0; k < 4; ++k) {
					two[twori++] = temp / (8 / pow(2, k));
					temp = temp % (8 / (int)(pow(2, k)));
				}
			}
			two[twori] = '\0';
			tbit = --twori;
			/* 下面进行二进制减一操作,以获得补码	*/
			if (two[twori] == 1)
				two[twori] = 0;
			else if (two[twori] == 0) {
				two[twori] = 1;
				for (--twori; two[twori] == 0; --twori)
					two[twori] = 1;
				two[twori] = 0;
			}
			/* 按位取反操作,以获得原码				*/ 
			for (k = 0; two[k] != '\0'; ++k)
				if (two[k] == 1)
					two[k] = 0;
				else if (two[k] == 0)
					two[k] = 1;
			/* 把二进制原码转十进制,以获得十进制数的绝对值		*/
			j = 0;
			for (k = tbit; k >= 0; --k) {
				num = num + two[k] * pow(2, j);
				j++;
			}
			/* 将上一步得到的int num乘-1,以获得正确的整型值	*/
			num = num * (0 - 1);
		}
		else if (i - 2 <= bit_16) {	/* 十六进制是否为正数		*/
			for (--i; i >= 2; --i) {
				if (s[i] >= '0' && s[i] <= '9')
					temp = s[i] - 48;
				else if (s[i] >= 'a' && s[i] <= 'f')
					temp = s[i] - 97 + 10;
				else if (s[i] >= 'A' && s[i] <= 'F')
					temp = s[i] - 65 + 10;
				else {
					printf("error: Contains illegal characters");
					normal = NO;
					return 0;
				}
				num = num + temp * pow(16, j);
				++j;
			}
		}
		else {
			printf("error: 选择的长度错误!\n");
			normal = NO;
			return 0;
		} 		
	}		
	else if(i > 2) {
		printf("error: Chars not beginning with '0x' or '0X'\n");
		normal = NO;
		return 0;
	} else {
		printf("error: Chars just have '0x' or '0X'\n");
		normal = NO;
		return 0;
	}
	return num;
}

程序还有一些问题,有些数据检测错误例如0XFFFFFC87;待续