使用标准库函数 strtol (推荐)
这是最简单、最安全、也是最常用的方法。strtol 函数可以将一个字符串表示的数字转换为长整型(long),并可以指定转换的基数(进制)。
函数原型
long strtol(const char *nptr, char **endptr, int base);
参数说明
nptr: 指向要转换的字符串的指针。endptr: 一个指向char*类型的指针,如果这个指针不是NULL,函数会在这个指针中存储转换停止后的位置,这对于检查字符串中是否有非法字符非常有用。base: 转换的基数(进制)。base是 0,函数会根据字符串的前缀自动判断进制:0x或0X开头 -> 16进制0开头 -> 8进制 (Octal)- 其他 -> 10进制 (Decimal)
base是 16,则直接将字符串当作16进制处理。base的取值范围是 0 或 2 到 36。
示例代码
#include <stdio.h>
#include <stdlib.h> // 必须包含此头文件才能使用 strtol
#include <errno.h> // 用于检查转换错误
int main() {
const char *hex_str1 = "1A3F"; // 没有0x前缀
const char *hex_str2 = "0x7B"; // 有0x前缀
const char *hex_str3 = "G12"; // 包含非法字符G
char *endptr; // 用于存放转换结束后的位置
long decimal_num;
// --- 示例1:转换没有0x前缀的16进制字符串 ---
// 将base设为16,明确告诉函数这是16进制
decimal_num = strtol(hex_str1, &endptr, 16);
// 检查转换是否成功
if (errno == ERANGE) {
printf("错误:数字超出long的范围,\n");
} else if (endptr == hex_str1) {
printf("错误:没有数字被转换,\n");
} else {
printf("字符串 \"%s\" 转换为10进制是: %ld\n", hex_str1, decimal_num);
}
// --- 示例2:转换有0x前缀的16进制字符串 ---
// 将base设为0,让函数自动识别前缀
decimal_num = strtol(hex_str2, &endptr, 0);
if (errno == ERANGE) {
printf("错误:数字超出long的范围,\n");
} else if (endptr == hex_str2) {
printf("错误:没有数字被转换,\n");
} else {
printf("字符串 \"%s\" 转换为10进制是: %ld\n", hex_str2, decimal_num);
}
// --- 示例3:处理包含非法字符的字符串 ---
decimal_num = strtol(hex_str3, &endptr, 16);
if (errno == ERANGE) {
printf("错误:数字超出long的范围,\n");
} else if (endptr == hex_str3) {
printf("错误:没有数字被转换,\n");
} else {
// strtol会转换到第一个非法字符为止
printf("字符串 \"%s\" 转换到 \"%s\" 时停止,转换结果是: %ld\n", hex_str3, endptr, decimal_num);
}
return 0;
}
输出结果:
字符串 "1A3F" 转换为10进制是: 6719
字符串 "0x7B" 转换为10进制是: 123
字符串 "G12" 转换到 "12" 时停止,转换结果是: 0
手动实现转换算法
如果你需要理解转换的原理,或者在没有标准库的环境下进行开发,可以手动实现这个算法,16进制转10进制的原理是按权展开求和。
对于一个16进制数 1A3F,其10进制值为:
1 * 16³ + A * 16² + 3 * 16¹ + F * 16⁰
= 1 * 4096 + 10 * 256 + 3 * 16 + 15 * 1
= 4096 + 2560 + 48 + 15
= 6719
算法步骤
- 初始化一个结果变量
decimal_value为 0。 - 初始化一个权重变量
power为 1 (代表 16⁰)。 - 从16进制数的最后一位开始向前遍历。
- 对于每一位字符:
a. 将字符转换为对应的数值('A' -> 10, 'F' -> 15, '9' -> 9)。
b. 将该数值乘以当前的权重
power。 c. 将结果累加到decimal_value中。 d. 将权重power乘以 16,为下一位做准备。 - 遍历结束后,
decimal_value就是最终的10进制结果。
示例代码
#include <stdio.h>
#include <ctype.h> // 用于 isxdigit 和 toupper
// 辅助函数:将单个16进制字符转换为对应的数值
int hexCharToValue(char c) {
c = toupper(c); // 统一转为大写,简化判断
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c >= 'A' && c <= 'F') {
return 10 + (c - 'A');
}
// 如果字符非法,返回-1
return -1;
}
// 手动函数:将16进制字符串转换为10进制整数
long hexToDecimal(const char *hex_str) {
long decimal_value = 0;
long power = 1; // 16的幂次,从16^0开始
// 从字符串末尾向前遍历
for (int i = strlen(hex_str) - 1; i >= 0; i--) {
char c = hex_str[i];
// 检查字符是否为有效的16进制字符
if (!isxdigit(c)) {
printf("错误:字符 '%c' 不是有效的16进制数,\n", c);
return -1; // 返回-1表示错误
}
int value = hexCharToValue(c);
decimal_value += value * power;
power *= 16;
}
return decimal_value;
}
int main() {
const char *hex_str1 = "1A3F";
const char *hex_str2 = "FF";
const char *hex_str3 = "12G"; // 包含非法字符
long result1 = hexToDecimal(hex_str1);
if (result1 != -1) {
printf("手动转换: 字符串 \"%s\" 转换为10进制是: %ld\n", hex_str1, result1);
}
long result2 = hexToDecimal(hex_str2);
if (result2 != -1) {
printf("手动转换: 字符串 \"%s\" 转换为10进制是: %ld\n", hex_str2, result2);
}
long result3 = hexToDecimal(hex_str3);
if (result3 != -1) {
printf("手动转换: 字符串 \"%s\" 转换为10进制是: %ld\n", hex_str3, result3);
}
return 0;
}
输出结果:
手动转换: 字符串 "1A3F" 转换为10进制是: 6719
手动转换: 字符串 "FF" 转换为10进制是: 255
错误:字符 'G' 不是有效的16进制数。
总结与对比
| 特性 | 方法一: strtol |
方法二: 手动实现 |
|---|---|---|
| 易用性 | 非常高,一行代码即可完成。 | 较低,需要编写较多代码。 |
| 健壮性 | 非常高,能正确处理前缀、非法字符、溢出等情况。 | 较低,需要自己处理各种边界条件和错误。 |
| 性能 | 通常非常快,由标准库高度优化。 | 取决于实现,但一般不如库函数。 |
| 适用场景 | 绝大多数实际应用场景,推荐首选。 | 学习目的、嵌入式系统、或需要特殊逻辑时。 |
| 依赖 | 需要包含 <stdlib.h> |
只需要基本的标准库,如 <stdio.h>, <ctype.h> |
在日常编程中,强烈推荐使用 strtol 函数,因为它简单、安全、可靠,手动实现的方法虽然有助于理解底层原理,但在生产代码中不推荐使用,因为它容易出错且维护成本高。
