std::stoul是最直接的选择,专为string→unsigned long设计,跳过前导空白、按十进制解析并抛异常;std::strtoul更细粒度但需手动检查errno和endptr;注意unsigned long跨平台位宽差异,避免使用atoi/atol。

std::stoul 是最直接的选择
C++11 起,std::stoul 专为 string → unsigned long 设计,会跳过前导空白、按十进制解析(除非指定基数),并抛出异常处理错误。
- 输入为空或只含空白时,抛出
std::invalid_argument - 数值超出
unsigned long表示范围时,抛出std::out_of_range - 基数默认为 10;若需解析十六进制(如
"0xFF"),传入0或16作为第三个参数 - 不接受带符号前缀(
"-123"会抛std::invalid_argument)
try {
std::string s = "4294967295";
unsigned long val = std::stoul(s); // OK on most systems
} catch (const std::invalid_argument& e) {
// 格式不对,比如 "abc" 或 ""
} catch (const std::out_of_range& e) {
// 比 ULONG_MAX 还大,比如 "18446744073709551616"
}用 std::strtoul 更细粒度地控制解析
std::strtoul 是 C 风格函数,不抛异常,适合需要避免异常开销或手动检查解析位置的场景。
- 返回值在溢出时为
ULONG_MAX,需配合errno判断是否真溢出 - 第二个参数(
char** endptr)可获取首个未转换字符地址,用于验证是否“全字符串都被成功解析” - 若输入以空格开头,它会跳过;但若中间有非法字符(如
"123abc"),endptr指向'a',此时需检查*endptr == '\0'才算完整转换
char* end;
errno = 0;
unsigned long val = std::strtoul(s.c_str(), &end, 10);
if (errno == ERANGE || *end != '\0' || end == s.c_str()) {
// 溢出 / 有尾随垃圾字符 / 完全没转换(空或纯空格)
}注意 unsigned long 的实际大小和平台差异
unsigned long 在不同平台上位宽不同:Linux x86_64 是 64 位,Windows MSVC x64 是 32 位。这意味着:
-
同样字符串
"4294967296"在 Windows 上会溢出(因ULONG_MAX == 4294967295),但在 Linux 上可能合法立即学习“C++免费学习笔记(深入)”;
若需跨平台稳定行为,优先考虑
std::stoull+unsigned long long,或明确使用固定宽度类型如uint64_t并配std::stoull不要依赖
sizeof(unsigned long) == 8做判断编译器宏如
<strong>LP64</strong>或_WIN64可用于条件编译,但更推荐用std::numeric_limits<unsigned long>::max()</unsigned>运行时检查边界
别用 std::atoi 或 std::atol 转 unsigned long
std::atoi 返回 int,std::atol 返回 long —— 二者均为有符号类型,且:
- 不报告错误(失败时返回 0,无法区分
"0"和"abc") - 无溢出检测机制
- 强转为
unsigned long会掩盖负数溢出(如"-1"转成极大正数)
这种隐式转换容易埋下静默 bug,尤其当输入来源不可控时。
实际转换逻辑里,只要涉及边界敏感或外部输入,就该放弃 atoi/atol,改用 stoul 或 strtoul。
跨平台项目里,unsigned long 的歧义比想象中更常触发问题——宁可多写一行 static_assert 或换用 uint64_t,也别靠文档赌平台行为。


















