使用标准库函数 strtol (最推荐)
这是最标准、最安全、也是最推荐的方法。strtol (string to long) 函数可以将一个字符串转换为长整型,并可以自动处理不同进制(包括十六进制)。
函数原型
long strtol(const char *str, char **endptr, int base);
参数说明
str: 要转换的字符串指针。endptr: 一个指向char*类型的指针,转换完成后,*endptr会指向第一个未能被转换的字符,如果这个参数是NULL,则不使用它。base: 转换的基数(进制)。base是 0,函数会根据字符串的前缀来判断进制:"0x"或"0X"开头:十六进制"0"开头:八进制- 其他情况:十进制
base是 16,则直接按十六进制转换,并且可以不要求字符串必须有"0x"前缀。
返回值
- 成功时,返回转换后的长整型值。
- 如果转换后的值超出
long类型的表示范围,函数会返回LONG_MAX或LONG_MIN,并设置全局变量errno为ERANGE。 - 如果第一个字符就不能转换,函数返回 0。
示例代码
#include <stdio.h>
#include <stdlib.h> // 包含 strtol 的头文件
#include <errno.h> // 包含 errno 的头文件
#include <limits.h> // 包含 LONG_MAX 的头文件
// 使用 strtol 函数进行十六进制转十进制的函数
long hex_to_dec_strtol(const char *hex_str) {
char *endptr; // 用于检查转换是否完全成功
long decimal_value;
// 设置 errno 为 0,以便检测溢出
errno = 0;
// 调用 strtol,base设为16表示十六进制
decimal_value = strtol(hex_str, &endptr, 16);
// 错误处理
if (errno == ERANGE) {
// 转换后的值超出 long 范围
printf("错误:数值超出范围!\n");
return -1; // 或者返回 LONG_MAX/LONG_MIN
} else if (endptr == hex_str) {
// 没有字符被转换,说明输入无效
printf("错误:无效的十六进制输入!\n");
return -1;
} else if (*endptr != '\0') {
// 字符串末尾还有未转换的字符("1A2G" 中的 'G')
printf("警告:字符串中存在非十六进制字符 '%c',已忽略,\n", *endptr);
}
return decimal_value;
}
int main() {
const char *hex_num1 = "1A"; // 26
const char *hex_num2 = "FF"; // 255
const char *hex_num3 = "0x7FFFFFFF"; // 2147483647 (32位系统上的 LONG_MAX)
const char *hex_num4 = "GHI"; // 无效输入
const char *hex_num5 = "123abc"; // 可以不带 0x 前缀
printf("字符串 \"%s\" 转换为十进制是: %ld\n", hex_num1, hex_to_dec_strtol(hex_num1));
printf("字符串 \"%s\" 转换为十进制是: %ld\n", hex_num2, hex_to_dec_strtol(hex_num2));
printf("字符串 \"%s\" 转换为十进制是: %ld\n", hex_num3, hex_to_dec_strtol(hex_num3));
printf("字符串 \"%s\" 转换为十进制是: %ld\n", hex_num4, hex_to_dec_strtol(hex_num4));
printf("字符串 \"%s\" 转换为十进制是: %ld\n", hex_num5, hex_to_dec_strtol(hex_num5));
return 0;
}
手动实现算法 (适合学习和面试)
如果你需要自己实现这个转换逻辑,或者想了解其背后的原理,可以手动编写一个函数,其核心思想是遍历字符串的每一个字符,根据其权重(16的幂次方)进行累加。
算法步骤
- 初始化一个结果变量
result为 0。 - 遍历字符串中的每一个字符,直到遇到字符串结束符
\0。 - 对于每个字符,检查它是否是一个有效的十六进制字符(
0-9,a-f,A-F)。 - 如果字符是
'0'到'9',其值为字符 - '0'。 - 如果字符是
'a'到'f',其值为字符 - 'a' + 10。 - 如果字符是
'A'到'F',其值为字符 - 'A' + 10。 - 如果字符无效,可以报错或停止转换。
- 更新
result的值:result = result * 16 + current_char_value。 - 返回最终的
result。
示例代码
#include <stdio.h>
#include <ctype.h> // 包含 isxdigit, toupper 等函数
// 手动实现的十六进制转十进制函数
// 返回 int 类型,假设输入是有效的且在 int 范围内
int hex_to_dec_manual(const char *hex_str) {
int result = 0;
int i = 0;
// 跳过可选的 "0x" 或 "0X" 前缀
if (hex_str[0] == '0' && (hex_str[1] == 'x' || hex_str[1] == 'X')) {
i = 2;
}
// 遍历字符串的其余部分
while (hex_str[i] != '\0') {
char c = toupper(hex_str[i]); // 统一转换为大写,方便处理
int value;
if (c >= '0' && c <= '9') {
value = c - '0';
} else if (c >= 'A' && c <= 'F') {
value = 10 + (c - 'A');
} else {
// 遇到非法字符,打印错误并停止转换
printf("错误:非法的十六进制字符 '%c'\n", hex_str[i]);
return -1; // 返回错误码
}
result = result * 16 + value;
i++;
}
return result;
}
int main() {
const char *hex_num1 = "1A"; // 26
const char *hex_num2 = "FF"; // 255
const char *hex_num3 = "0x7F"; // 127
const char *hex_num4 = "123abc"; // 1194684
printf("字符串 \"%s\" 转换为十进制是: %d\n", hex_num1, hex_to_dec_manual(hex_num1));
printf("字符串 \"%s\" 转换为十进制是: %d\n", hex_num2, hex_to_dec_manual(hex_num2));
printf("字符串 \"%s\" 转换为十进制是: %d\n", hex_num3, hex_to_dec_manual(hex_num3));
printf("字符串 \"%s\" 转换为十进制是: %d\n", hex_num4, hex_to_dec_manual(hex_num4));
return 0;
}
使用 sscanf (简单直接)
sscanf 是一个强大的格式化输入函数,可以从字符串中读取格式化数据,你也可以用它来转换进制。
函数原型
int sscanf(const char *str, const char *format, ...);
示例代码
#include <stdio.h>
// 使用 sscanf 函数进行转换
int hex_to_dec_sscanf(const char *hex_str) {
int decimal_value;
// %x 是 sscanf 中用于读取十六进制数的格式说明符
// sscanf 会尝试从 hex_str 中读取一个十六进制数存入 decimal_value
// 返回成功赋值的字段数量,成功应该是 1
int items_read = sscanf(hex_str, "%x", &decimal_value);
if (items_read != 1) {
printf("错误:无效的十六进制输入!\n");
return -1;
}
return decimal_value;
}
int main() {
const char *hex_num1 = "1A"; // 26
const char *hex_num2 = "FF"; // 255
const char *hex_num3 = "0x7F"; // 127 (sscanf 的 %x 能自动识别 0x 前缀)
const char *hex_num4 = "GHI"; // 无效输入
printf("字符串 \"%s\" 转换为十进制是: %d\n", hex_num1, hex_to_dec_sscanf(hex_num1));
printf("字符串 \"%s\" 转换为十进制是: %d\n", hex_num2, hex_to_dec_sscanf(hex_num2));
printf("字符串 \"%s\" 转换为十进制是: %d\n", hex_num3, hex_to_dec_sscanf(hex_num3));
printf("字符串 \"%s\" 转换为十进制是: %d\n", hex_num4, hex_to_dec_sscanf(hex_num4));
return 0;
}
总结与对比
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
strtol |
最安全、最灵活,能处理溢出、无效字符、前缀,错误处理机制完善。 | 语法稍复杂,需要理解 endptr 和 errno。 |
生产环境、健壮的程序、库函数的首选。 |
| 手动实现 | 易于理解,能深入掌握转换原理,不依赖任何库函数。 | 代码量多,需要自己处理所有边界情况(如前缀、非法字符、溢出等),容易出错。 | 学习、算法练习、面试。 |
sscanf |
代码简洁,一行代码即可完成转换。 | 错误处理能力较弱,无法像 strtol 那样精确地知道哪里出了问题,对输入格式的要求可能更严格。 |
快速原型开发、输入格式确定且简单的场景。 |
对于绝大多数实际应用,请优先使用 方法一 (strtol),它是最专业、最安全的选择,手动实现的方法虽然不推荐用于生产代码,但对于学习和理解底层原理非常有帮助。sscanf 是一个方便的工具,但在需要严格错误验证的场合,strtol 更胜一筹。
