c/c++函数 -strtol strtoll strtoul strtoull 使用注意项cc

本文介绍 C/C++ 中字符串转整型的常用函数,如 strtol、strtoll、strtoul 和 strtoull 的使用方法及注意事项。通过封装简化函数调用流程,并提供错误处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说明

在项目开发时,字符串跟整形的转换是普遍需求的一个功能,在c/c++中常用的几个函数包括
strtol strtoll strtoul strtoull。
目前我们的项目里面进行转换的时候都是直接调用,对被转换的字符串是否有效,转换是否成功都没有一个基本的判断,一直想研究下,今天正好有空,仔细看了下linux man page中这几个函数的详细解释.

声明

#include <stdlib.h>
long int strtol(const char *nptr, char **endptr, int base);
long long int strtoll(const char *nptr, char **endptr, int base);
unsigned long int strtoul(const char *nptr, char **endptr, int base);
unsigned long long int strtoull(const char *nptr, char **endptr, int base);

功能:

这一组函数根据指定的进制base(2-36或者0,为0时根据字符串自动推断转换的进制)将nptr指向的字符串转换为对应的整形。
nptr可以以多个空格(isspace函数返回true的字符)开头,接着也可以有'+'或者'-'正负符号标记。如果base是16或者是0,接下来可以有“0x”的前缀,此时字符串将会当作16进制数字进行转换;如果接下的字符是‘0’,将会被当作8进制进行转换;除此之外都会被当作默认的十进制。
除了前缀之外其余的字符串将会被转换为对应的整形,并且会在给定进制base的第一个非有效数字字符终止(对于10以上的进制,'A'表示10,‘11’表示B,以此类推,'Z'表示35)。
如果endptr是非空,则endptr将会保存nptr中第一个指定进制base的无效字符串的地址;如果不存在有效字符串,则endptr会保存原始nptr的地址,并且返回0。通常情况,如果*nptr值不为'\0',返回时**endptr值为'\0',整个nptr字符串都是无效的。

返回值:

返回对应的转换结果。对于结果溢出的现象,分别返回对应的整形的最大值,或者最小值(LONG_MAX LONG_MIN LLONG_MAX LLONG_MIN ULONG_MAX ULLONG_MAX ULLONG_MAX),此时errno被置为ERANGE.对于转换无符号整形的几个函数,如果字符串有前置的减号标记,转换的结果仍会被转换无无符号整形。

ERRORS:

EINVAL base值不支持
ERANGE 结果溢出
有的实现在转换没有进行时(没有数字字符输入,返回0),也可能将errno设置为EINVAL。

注意:

因为这一组函数,在成功或者失败的情况下,都有可能合理的返回0或者溢出极值,调用者在使用之前应该将errno设置为0,并且在调用之后检查errno的值来确定是否有错误发生。
按照上面的文档对几个函数进行封装,便于在实践中使用,具体如下:

封装:

bool stringToI32(const std::string &str, int32_t  &val)
{
	long temp_val = 0;
	bool isOK = stringToL(str, temp_val);
	val = temp_val;
	return isOK && (temp_val >= -0x7fffffff && temp_val <= 0x7fffffff/*32bit整形的有效范围*/);
}

bool stringToU32(const std::string &str, uint32_t  &val)
{
	unsigned long temp_val = 0;
	bool isOK = stringToUL(str, temp_val);
	val = temp_val;
	return isOK && (temp_val <= 0xffffffff/*32bit无符号整形的有效范围*/);
}

bool stringToI64(const std::string &str, int64_t  &val)
{
	long long temp_val = 0;
	bool isOK = stringToLL(str, temp_val);
	val = temp_val;
	return isOK && (temp_val >= -0x7fffffffffffffff && temp_val <= 0x7fffffffffffffff/*64bit整形的有效范围*/);
}

bool stringToU64(const std::string &str, uint64_t  &val)
{
	unsigned long long temp_val = 0;
	bool isOK = stringToULL(str, temp_val);
	val = temp_val;
	return isOK && (temp_val <= 0xffffffffffffffff/*64bit无符号整形的有效范围*/);
}

bool stringToL(const std::string &str, long  &val)
{
	bool isOK = false;
	const char *nptr = str.c_str();
	char *endptr = NULL;
	errno = 0;
	val = strtol(nptr, &endptr, 10);
	//error ocur
	if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
		|| (errno != 0 && val == 0))
	{

	}
	//no digit find
	else if (endptr == nptr)
	{

	}
	else if (*endptr != '\0')
	{
		// printf("Further characters after number: %s\n", endptr);
	}
	else
	{
		isOK = true;
	}

	return isOK;
}

bool stringToLL(const std::string &str, long long &val)
{
	bool isOK = false;
	const char *nptr = str.c_str();
	char *endptr = NULL;
	errno = 0;
	val = strtoll(nptr, &endptr, 10);
	//error ocur
	if ((errno == ERANGE && (val == LLONG_MAX || val == LLONG_MIN))
		|| (errno != 0 && val == 0))
	{

	}
	//no digit find
	else if (endptr == nptr)
	{

	}
	else if (*endptr != '\0')
	{
		// printf("Further characters after number: %s\n", endptr);
	}
	else
	{
		isOK = true;
	}

	return isOK;
}

bool stringToUL(const std::string &str, unsigned long  &val)
{
	bool isOK = false;
	const char *nptr = str.c_str();
	char *endptr = NULL;
	errno = 0;
	val = strtoul(nptr, &endptr, 10);
	//error ocur
	if ((errno == ERANGE && (val == ULONG_MAX))
		|| (errno != 0 && val == 0))
	{

	}
	//no digit find
	else if (endptr == nptr)
	{

	}
	else if (*endptr != '\0')
	{
		// printf("Further characters after number: %s\n", endptr);
	}
	else
	{
		isOK = true;
	}

	return isOK;
}

bool stringToULL(const std::string &str, unsigned long long &val)
{
	bool isOK = false;
	const char *nptr = str.c_str();
	char *endptr = NULL;
	errno = 0;
	val = strtoull(nptr, &endptr, 10);
	//error ocur
	if ((errno == ERANGE && (val == ULLONG_MAX))
		|| (errno != 0 && val == 0))
	{

	}
	//no digit find
	else if (endptr == nptr)
	{

	}
	else if (*endptr != '\0')
	{
		// printf("Further characters after number: %s\n", endptr);
	}
	else
	{
		isOK = true;
	}

	return isOK;
}


<think>嗯,用户想了解C语言中strtol函数的用法和功能,特别是关于字符串转整数的实现。用户引用了站内三个资料片段,其中[2]和[3]都提到了strtol,说明用户可能已经在尝试理解这个函数,但需要更系统的讲解。用户身份可能是C语言初学者或中级开发者,正在处理字符串转换相关的编程任务。深层需求应该不只是知道基本用法,而是想掌握如何正确使用strtol处理复杂情况(比如进制转换、错误检测),避免常见的陷阱。从引用[3]看,用户似乎还注意到16进制转换的场景,这可能涉及网络编程(如MAC地址处理)。所以讲解时既要覆盖基础,也要强调实际应用中的注意。我会结构化回答:先定义函数原型并拆解每个参数,然后用最简单的十进制转换示例建立直观认知。重点要说明三个关键特性:自动跳过空白字符、灵活支持2-36进制、通过endptr捕获无效字符位置。用户引用的MAC地址转换示例很典型,可以转化为代码案例展示16进制处理。错误处理部分必须强调strtol和atoi的本质区别——strtol能精确反馈转换状态,这对健壮性要求高的程序至关重要。最后总结适用场景时,要对比atoi和sscanf,帮用户建立选择标准。用户可能产生的疑问点:当字符串存在非数字后缀时endptr的行为、ERANGE的处理时机、如何避免常见的内存错误。这些都要在示例中预判性解答。</think> 好的,`strtol` 是 C 标准库 (`stdlib.h`) 中一个非常强大的将字符串转换为长整型 (`long int`) 的函数。相比于 `atoi` 或 `atol`,`strtol` 提供了更强的健壮性和灵活性,特别是可以指定进制和检测转换错误。 ### `strtol` 函数详解 1. **函数原型:** ```c #include <stdlib.h> // 必需的头文件 long int strtol(const char *str, char **endptr, int base); ``` * **`str`:** 指向要转换的以 null 结尾的字符串(数字序列)的指针。 * **`endptr`:** 指向 `char*` 类型的指针的地址。函数会将转换停止的字符(第一个无法识别的字符)的地址存储在这个指针指向的位置。如果不需要该信息,可以传递 `NULL`。 * **`base`:** 指定期望的数字的**进制基数**(底数),范围在 2 到 36 之间,或者是特殊的 0。这个参数至关重要,它决定了函数如何解析字符串中的数字字符。 2. **`base` 参数详解(核心功能):** * **`base = 0`:** 让 `strtol` 根据字符串的格式**自动判断进制**。 * 如果字符串以 `0x` 或 `0X` 开头,则解释为十六进制 (16)。 * 如果字符串以 `0` 开头(并且不是 `0x`/`0X`),则解释为八进制 (8)。 * 其他情况(不以 `0` 开头,或者不是数字开头但符合下述规则),则解释为十进制 (10)。 * **`base = 2`** 到 **`base = 36`:** 明确指定数字字符串的进制。 * 对于进制 `n` (2 <= n <= 36),合法的数字字符包括: * `'0'` 到 `'9'`(表示 0 到 9)。 * `'a'` 到 `'z'` 或 `'A'` 到 `'Z'`(表示 10 到 35)。在解释时,字母的大小写不敏感。例如,在十六进制 (`base=16`) 中,`'a'` 和 `'A'` 都表示数字 10 (0xA)。 * 例如:`base=16` 允许字符 `0-9`, `a-f`, `A-F`;`base=2` 只允许字符 `'0'` 和 `'1'`。 3. **解析过程:** 1. 跳过字符串开头的**所有空白字符**(空格、制表符、换行符等)。 2. 尝试识别一个**可选的正负号** (`+` 或 `-`)。 3. 根据 `base` 参数指定的规则,开始逐个字符解释数字,将其组合成一个整数。 * 当遇到一个在该进制下**不合法**的字符时(或者到达字符串结尾的 `\0`),转换过程停止。 4. 将转换得到的**长整型结果**返回。 5. 如果 `endptr` 不是 `NULL`,则将第一个导致转换停止的字符的**地址**存储在 `*endptr` 指向的指针变量中。 * 如果整个字符串都被成功转换(转换在 `\0` 处停止),则 `*endptr` 会被赋值为指向 `\0`(字符串结束符)的位置。 * 如果字符串开头就没有合法数字序列(或者全是空白字符后接着非法字符),则 `str`` 的起始地址会被存储在 `*endptr` 中(意味着转换没有实际开始)。 4. **错误处理与边界:** * 如果转换得到的数值超出了 `long int` 能表示的**范围**(上溢或下溢),函数会: * 返回 `LONG_MAX`(如果值是正的)或 `LONG_MIN`(如果值是负的)。 * 并将全局变量 `errno` 设置为 `ERANGE`(表示范围错误)。**使用后需检查 `errno` 是否等于 `ERANGE` 来判断是否发生溢出。** * 如果字符串中根本**没有数字**或**没有有效的数字序列**(例如 `"abc"` 或 `" "`),函数会返回 **0**。 * 此时,可以结合 `endptr` 的值判断是否真的应该得到 0:如果 `*endptr == str`(即第一个未转换的字符就是原始字符串的开头),说明开头就没有可转换的数字,这个返回的 0 是无效的。 5. **用法示例:** ```c #include <stdio.h> #include <stdlib.h> #include <errno.h> // 用于 errno 和 ERANGE #include <limits.h> // 用于 LONG_MIN, LONG_MAX int main() { const char *str1 = "12345"; // 明确的十进制 const char *str2 = " -0x1A3"; // 自动识别十六进制(带空格和负号) const char *str3 = "101010"; // 二进制(需指定base=2) const char *str4 = "755"; // 八进制(base=8 或 base=0自动识别开头0?注意755开头不是0,所以base=0是十进制) const char *str5 = "123abc"; // 部分有效(十进制,abc非法) const char *str6 = "abc123"; // 无效开头 const char *str7 = "9999999999999999999999"; // 可能溢出(取决于系统long大小) char *endptr; // 用于接收 endptr 的指针变量 long int num; // 示例 1: 十进制转换 num = strtol(str1, &endptr, 10); printf("\"%s\" -> %ld | Endptr: '%s'\n", str1, num, endptr); // 输出: "12345" -> 12345 | Endptr: '' // 示例 2: 自动识别十六进制 (base=0) num = strtol(str2, &endptr, 0); printf("\"%s\" -> %ld | Endptr: '%s'\n", str2, num, endptr); // 输出: " -0x1A3" -> -419 | Endptr: '' // 示例 3: 二进制转换 (base=2) num = strtol(str3, &endptr, 2); printf("\"%s\" -> %ld | Endptr: '%s'\n", str3, num, endptr); // 输出: "101010" -> 42 | Endptr: '' // 示例 4: 八进制转换 (需明确指定base=8) num = strtol(str4, &endptr, 8); printf("\"%s\" -> %ld | Endptr: '%s'\n", str4, num, endptr); // 输出: "755" -> 493 | Endptr: '' // 示例 5: 部分转换 (base=10) num = strtol(str5, &endptr, 10); printf("\"%s\" -> %ld | Endptr: '%s'\n", str5, num, endptr); // 输出: "123abc" -> 123 | Endptr: 'abc' // 示例 6: 无数字开头 (base=10) num = strtol(str6, &endptr, 10); if (endptr == str6) { // 关键: 检查转换是否根本没开始 printf("\"%s\" -> Invalid (no digits) | Endptr: '%s'\n", str6, endptr); } else { printf("\"%s\" -> %ld | Endptr: '%s'\n", str6, num, endptr); } // 输出: "abc123" -> Invalid (no digits) | Endptr: 'abc123' // 示例 7: 溢出检测 (base=10) errno = 0; // 重置 errno num = strtol(str7, &endptr, 10); if (errno == ERANGE) { if (num == LONG_MAX) { printf("\"%s\" -> Overflow (LONG_MAX: %ld)\n", str7, LONG_MAX); } else if (num == LONG_MIN) { printf("\"%s\" -> Underflow (LONG_MIN: %ld)\n", str7, LONG_MIN); } } else if (endptr == str7) { printf("\"%s\" -> Invalid\n", str7); } else { printf("\"%s\" -> %ld\n", str7, num); } // 输出(大概率): "9999999999999999999999" -> Overflow (LONG_MAX: 9223372036854775807) return 0; } ``` 6. **关键优势总结(相比 `atoi`):** * **错误检测:** 能够检测溢出(通过返回值和 `errno=ERANGE`)和输入无效的情况(通过返回 0 和 `endptr` 的比较)。 * **进制支持:** 支持多种进制 (2-36) 和自动识别 (`base=0`)。 * **部分转换:** 能够转换字符串开头有效的数字部分,并通过 `endptr` 定位剩余的无效字符,用于后续处理。 * **灵活解析:** 允许字符串开头存在空白字符和可选的正负号。 7. **相关函数:** * `strtoll`: 将字符串转换为 `long long int` 类型(处理更大的整数范围)。 * `strtoul`: 将字符串转换为 `unsigned long int` 类型(无符号长整型)。 * `strtoull`: 将字符串转换为 `unsigned long long int` 类型(无符号长长整型)。 * `atoi`: 更简单的十进制转换,但缺乏错误处理。本质类似 `strtol(str, NULL, 10)`。 ### 总结 `strtol` 是 C 语言中处理**安全、可靠、灵活的字符串到整数转换**的首选函数。它在解析 MAC 地址、读取配置文件中的数字、解析命令行参数数字等涉及不同进制或需要严格验证的场景中尤其有价值[^3]。理解和正确使用其 `base` 参数、`endptr` 参数以及对 `errno` 的检查是掌握 `strtol` 的关键。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值