我对通过函数strtol(),strtoll() , strtoul(),strtoull()将 C 字符串转换为整数类型的算法感兴趣。
让我们从 C 语言标准(n1570, 7.22.1.4)开始:
、
strtol、strtoll和函数[strtoul... ] 5 如果主题序列具有预期的形式并且 的值为零,则根据 6.4.4.1 的规则,从第一位数字开始的字符序列被解释为整数常量. 如果主题序列具有预期的形式并且 的值介于 2 和 36 之间,则将其用作转换的基础,将其值赋予每个字母,如上所示。如果主题序列以减号开头,则转换产生的值被否定(在返回类型中)。指向最终字符串的指针存储在 指向的对象中,前提是它不是空指针。 [... ] , ,strtoull
long int strtol(const char * restrict nptr, char ** restrict endptr, int base);basebaseendptrendptr
Returnsstrtolstrtollstrtoul, 并且strtoull函数返回转换后的值(如果有)。如果无法执行转换,则返回零。如果正确的值在可表示值的范围之外,LONG_MIN,LONG_MAX,LLONG_MIN,LLONG_MAX,ULONG_MAX, 或者ULLONG_MAX返回(根据返回类型和值的符号,如果有的话),宏的值ERANGE存储在errno.
据我了解所写内容,想法如下(不考虑非十进制基数的前导空格字符和数字系统):
- 在源代码行中,我们将丢弃减号(如果有)。
- 如果字符串包含目标类型无法表示的数字,则返回
LONG_MIN,LONG_MAX依此类推。取决于目标类型和字符串中数字的符号。并且还安装errno在ERANGE. - 让我们将字符串转换为目标类型。
- 如果源字符串中的数字以减号开头,则将减号应用于目标类型中的值。
示例 #1
errno = 0;
unsigned long long ullval = std::strtoull("-18446744073709551615", nullptr, 10);
cout << "errno: " << errno << endl;
cout << "ullval: " << ullval << endl;
cout << -18446744073709551615ULL << endl;
结论:
errno: 0
ullval: 1
1
现在没关系。
"-18446744073709551615"减号已从行中删除。- 字符串
"18446744073709551615"中的数字由 type 表示unsigned long long。 - 让我们将字符串转换为数字。
- 对结果类型中的值应用减号:
-18446744073709551615ULL == 1ULL。
示例 #2
和上一个类似,只是我们转换了字符串"-18446744073709551616"。结论:
errno: 34
ullval: 18446744073709551615
我们也得到了预期的输出。
"-18446744073709551616"减号已从行中删除。- 字符串
"18446744073709551616"中的数字不能用type表示unsigned long long。因此,我们改变errno,并返回ULLONG_MAX。
示例#3
errno = 0;
long long llval = std::strtoll("-9223372036854775808", nullptr, 10);
cout << "errno: " << errno << endl;
cout << "llval: " << llval << endl;
结论:
errno: 0
llval: -9223372036854775808
现在问题出现了。
"-9223372036854775808"减号已从行中删除。- 字符串
"9223372036854775808"中的数字不能用type表示long long。因此,有必要安装errno并返回LLONG_MIN。
为什么函数std::strtoll()返回LLONG_MIN但没有设置errno?(代码示例的链接:g++,clang)
此外,Visual Studio 2010 中的以下代码(我现在手头没有更新的代码):
errno = 0;
unsigned long ulval = std::strtoul("-4294967296", nullptr, 10);
cout << "errno: " << errno << endl;
cout << "ulval: " << ulval << endl;
产生以下输出:
errno: 34
ulval: 1
"-4294967296"丢弃减号后字符串中的数字不能用类型表示unsigned long,因此必须设置errno(发生),返回值ULONG_MAX,但函数返回1。
strtol()根据语言标准,通过函数/strtoll()及其无符号对应项将字符串转换为整数的算法是什么。我试图理解标准中的内容并没有成功。
嗯,有人投票结束了这个问题,原因是“由错字引起的问题或不可重现的问题”。
可能值得关闭,但首先,如果“问题没有重现”,那么我希望看到一个编译器示例,在执行代码后std::strtoll("-9223372036854775808", nullptr, 10),它将设置errno为非零值。
如果errno不应该设置它,那么我很乐意阅读为什么不应该设置它的解释(当然,参考标准)。
该标准没有描述算法,因此,它描述了行为,即在python方面,不是鸭子的排列方式,而是鸭子应该如何嘎嘎;某些输入值的结果应该是什么(即使文本看起来像一系列动作)。
据我了解,7.22.1.4/5 中的短语“*该值...被否定(在返回类型中)*”表示对于转换过程中获得的值,
val结果必须与-val该类型等效。该语句是普遍接受的解释的基础,即无符号函数如果字符串以减号开头,则应返回一个应用一元减号的值而不是错误。是的,如果我们仅基于标准的文本,那么恕我直言,这样的解释有些牵强,并不完全符合最小意外原则,但事情就是这样发生的。在 linux mana 中,对此进行了更清楚的描述:此外,7.22.1.4/5要求所有函数,如果字符串中给出的有效数字超出该类型的可表示值的范围,则必须返回相应的常量和错误。
因此,
strtoul它应该返回(对于 32 位long,ULONG_MAX==4294967295):-∞...-4294967296→ULONG_MAX;errno=ERANGE-4294967295……-1→-val0……4294967295→val4294967296...∞→ULONG_MAX;errno=ERANGEA,
strtol应该返回 (whenLONG_MIN==-2147483648, aLONG_MAX==2147483647):-∞...-2147483649→LONG_MIN;errno=ERANGE-2147483648……2147483647→val2147483648...∞→LONG_MAX;errno=ERANGE同样对于
strtoull/strtoll。哪种实现算法将实现这种行为是他们的事。例如,您可以在 BSD libc: 中看到
strtoul实现strtol。问题中的所有示例都与这些值相匹配……当然,除了 msvc,但臭名昭著的是,它对符合标准并不十分讲究。