std::stoul 和 std::stoull 能自动解析带 0x/0x 前缀的十六进制字符串,基数设为 0 即可;无前缀如 "ff" 会被当作十进制解析,非法字符或溢出则抛出对应异常。

std::stoul 或 std::stoull 能直接解析 0x 前缀的十六进制字符串
只要字符串带 0x 或 0X 前缀,std::stoul 就能自动识别为十六进制,无需额外设置基数。这是最省事的方式,适合格式可控的输入。
常见错误现象:std::stoul("FF") 会按十进制解析,结果是 111,不是 255;必须写成 "0xFF" 才行。
- 支持前缀:
"0x1A"、"0Xff"、"0x0"都合法 - 不支持无前缀纯字母数字(如
"1A"),此时会当作十进制处理 - 超出
unsigned long范围时抛出std::out_of_range - 含非法字符(如空格、'G')时抛出
std::invalid_argument
示例:
try {
unsigned long val = std::stoul("0xFF", nullptr, 0); // 第三个参数为 0 表示自动推导进制
std::cout <h3>用 std::stoi/stol/stoll + 基数 16 解析无前缀十六进制字符串</h3><p>当输入是纯十六进制字符(如 <code>"1A"</code>、<code>"deadbeef"</code>),没有 <code>0x</code> 前缀时,必须显式传入基数 16,并确保第三个参数是 <code>0</code> 或 <code>16</code> —— 这里容易混淆。</p><p>关键点:第三个参数是 <code>base</code>,不是“是否启用前缀检测”。设为 <code>0</code> 会启用前缀检测(即只认 <code>0x</code>);设为 <code>16</code> 才强制按十六进制解析,忽略前缀。</p>
-
std::stoul("1A", nullptr, 16)→ 正确得到 26 -
std::stoul("1A", nullptr, 0)→ 按十进制解析,得 16 - 大小写不敏感:
"aF"和"AF"都合法 - 允许前导空格,但遇到第一个非空白/非十六进制字符就停(如
"1Axyz"解析出 26,不报错)
std::from_chars 是 C++17 推荐的无异常、零分配方案
如果追求性能或不能抛异常(嵌入式、实时系统),std::from_chars 是更现代的选择。它不依赖 locale,不抛异常,也不构造临时 string 对象。
注意:它不识别 0x 前缀,只解析纯十六进制数字序列,且要求传入缓冲区首尾指针。
- 返回值是
std::from_chars_result,含ptr(解析结束位置)和ec(错误码) - 错误码
std::errc::invalid_argument表示无有效数字;std::errc::result_out_of_range表示溢出 - 必须手动跳过前缀:
if (s.starts_with("0x")) s.remove_prefix(2);(C++20)或用指针偏移 - 只支持整型,不支持浮点;不接受空格
示例:
std::string_view s = "1A";
unsigned int val;
auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), val, 16);
if (ec == std::errc{}) {
// 成功,val == 26
}
手写循环解析适合教学、调试或极端受限环境
标准库函数在某些交叉编译环境(如裸机、旧 libc)可能不可用,或你想完全掌控解析逻辑(比如跳过特定分隔符、容忍下划线)。这时一个 10 行以内的循环足够可靠。
核心逻辑:遍历每个字符,用查表或 std::isdigit/std::tolower 判断,累加 val = val * 16 + digit,同时检查溢出。
- 查表比条件判断快:
static constexpr int kHexVal[256] = {...}可避免分支 - 溢出检查不能只靠最终结果:每次乘加前需判断
val > (UINT32_MAX - digit) / 16 - 忽略大小写只需
std::tolower(c),但注意它对非 ASCII 字符行为未定义 - 不处理前缀和空格,这些要调用方提前清理
真正麻烦的从来不是转换本身,而是错误输入的界定:你是否接受 "0x" 后无数字?是否允许中间出现 '_'?这些边界往往比算法更消耗调试时间。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











