如何在C语言中实现带可选0x前缀的十六进制字符串转十进制?
十六进制字符串转整数(htoi)的前缀处理实现
针对你在《The C Programming Language》练习2.3中遇到的0x/0X前缀处理问题,我们可以通过以下步骤优化代码,同时简化实现逻辑:
核心思路
- 前缀判断:先检查字符串开头是否存在可选的
0x或0X前缀,若存在则跳过这两个字符,从第三个字符开始处理。 - 简化数值计算:无需反转字符串,直接从左到右遍历累积计算——每一步将当前结果乘以16,再加上当前字符对应的十六进制值,逻辑更直观且节省内存。
- 修正合法性校验:校验时要排除前缀的
0和x/X,只校验前缀之后的字符(无前缀则校验全部)。
修改后的完整代码
#include <stdio.h> #include <string.h> #include <ctype.h> // 校验十六进制字符串合法性(支持0x/0X前缀) int check_valid(char *s) { int len = strlen(s); int start = 0; // 跳过可选前缀 if (len >= 2 && s[0] == '0' && (tolower(s[1]) == 'x')) { start = 2; // 前缀后无字符的情况直接判定无效 if (start >= len) return 0; } // 校验剩余字符 for (int i = start; i < len; i++) { if (!isdigit(s[i]) && !(tolower(s[i]) >= 'a' && tolower(s[i]) <= 'f')) { return 0; } } return 1; } // 单个十六进制字符转对应整数值 int rawint(char c) { if (isalpha(c)) { return toupper(c) - 'A' + 10; } else { return c - '0'; } } // 十六进制字符串转整数,处理0x/0X前缀 int htoi(char *s) { int len = strlen(s); int dec = 0; int start = 0; // 跳过前缀 if (len >= 2 && s[0] == '0' && (tolower(s[1]) == 'x')) { start = 2; } // 从左到右累积计算 for (int i = start; i < len; i++) { dec = dec * 16 + rawint(s[i]); } return dec; } int main() { // 测试用例:带前缀、不带前缀、大写前缀都可以 char s1[] = "7abc"; char s2[] = "0x7abc"; char s3[] = "0X1A3F"; char s4[] = "0x"; // 无效用例 // 测试s1 if (check_valid(s1)) { printf("%s -> %d\n", s1, htoi(s1)); } else { printf("%s 无效\n", s1); } // 测试s2 if (check_valid(s2)) { printf("%s -> %d\n", s2, htoi(s2)); } else { printf("%s 无效\n", s2); } // 测试s3 if (check_valid(s3)) { printf("%s -> %d\n", s3, htoi(s3)); } else { printf("%s 无效\n", s3); } // 测试s4 if (check_valid(s4)) { printf("%s -> %d\n", s4, htoi(s4)); } else { printf("%s 无效\n", s4); } return 0; }
关键修改点说明
- 前缀处理:在
check_valid和htoi函数中,都先判断字符串长度是否≥2,且前两个字符是0x或0X,如果是就将起始索引设为2,跳过前缀。 - 移除反转逻辑:原来的
reverse函数完全不需要,通过dec = dec * 16 + rawint(s[i])的方式,直接从左到右计算十六进制值,逻辑更简洁且避免了动态内存分配的开销。 - 合法性校验优化:校验时跳过前缀后,若剩余无字符(比如单独的
0x)直接返回无效;同时用tolower统一处理大小写,减少重复判断。 - 测试用例扩展:新增了带前缀、大写前缀、无效前缀的测试用例,覆盖更多场景。
内容的提问来源于stack exchange,提问作者devMe
相关产品推荐
相关产品推荐

