strtol 等人的规范中的混淆语言

Posted

技术标签:

【中文标题】strtol 等人的规范中的混淆语言【英文标题】:Confusing language in specification of strtol, et al 【发布时间】:2011-10-05 18:39:22 【问题描述】:

strtol 的规范在概念上将输入字符串分为“初始空白”、“主题序列”和“最终字符串”,并将“主题序列”定义为:

输入字符串的最长初始子序列,从第一个具有预期形式的非空白字符开始。如果输入字符串为空或完全由空白字符组成,或者如果第一个非空白字符不是符号或允许的字母或数字,则主题序列不应包含字符。

曾经我认为“最长初始子序列”业务类似于scanf 的工作方式,其中"0x@" 将扫描为"0x",这是一个失败的匹配,然后是"@" 作为下一个未读字符.然而,经过一番讨论,我基本确信strtol 处理的是预期形式的最长初始子序列,而不是最长的初始字符串,它是预期形式的某些可能字符串的初始子序列。

让我感到困惑的是规范中的这种语言:

如果主题序列为空或不具有预期形式,则不进行转换; str 的值存储在 endptr 指向的对象中,前提是 endptr 不是空指针。

如果我们接受看似正确的“主题序列”定义,则不存在没有预期形式的非空主题序列,而是(避免冗余和混淆)文本应该只是阅读:

如果主题序列为空,则不进行转换; str 的值存储在 endptr 指向的对象中,前提是 endptr 不是空指针。

谁能为我澄清这些问题?也许指向过去讨论或任何相关缺陷报告的链接会很有用。

【问题讨论】:

一个简单的规范示例就可以解决所有问题... 确实如此。我对这样的事情有一种感觉,委员会实际上是在试图避免直言不讳,因为担心会引发一场关于它应该如何表现的疯狂争论...... 【参考方案1】:

我完全同意您的评估:根据定义,所有非空主题序列都是预期形式,因此标准的措辞是可疑的。

在浮点转换函数的情况下,还有另一个错误(C99:TC3 第 7.20.1.3 节,§3):

[...] 主题序列被定义为最长的初始 输入字符串的子序列,从第一个开始 非空白字符,即预期形式。主题 如果输入字符串不是 预期形式。

这意味着整个输入字符串必须是预期的形式,这违背了endptr 参数的目的。有人可能会争辩说,输入字符串的预期形式与主题序列的预期形式不同,但它仍然很混乱。

strto*()*scanf() 系列函数的语义不同,这也是正确的:如果两者都匹配,它们将始终在值上达成一致并使用相同数量的字符(以及它们所在的任何 libc 实现) do not 已损坏,包括我上次检查时的 newlib 和 glibc),但 *scanf() 也无法匹配需要回溯多个字符的情况,例如您的示例 "0x@""1.0e+"

【讨论】:

【参考方案2】:

如果您从 C9​​9 标准的 §7.20.1.4(strtol、strtoll、strtoul 和 strtoull 函数)¶2 而非 ¶4 开始,可能会更容易理解:

¶2 strtol、strtoll、strtoul 和 strtoull 函数将初始 nptr 指向的字符串的一部分到 long int, long long int, unsigned long int 和 unsigned long long int 分别表示。第一的, 他们将输入字符串分解为三个部分:一个初始的,可能是空的,序列 空白字符(由 isspace 函数指定),主题序列 类似于一个整数,以由 base 的值确定的某个基数表示,以及 一个或多个无法识别的字符的最终字符串,包括终止的 null 输入字符串的字符。然后,他们尝试将主题序列转换为 整数,并返回结果。

¶3 如果 base 的值为零,则主题序列的预期形式是 如 6.4.4.1 中所述的整数常量,前面可选加号或减号,但 不包括整数后缀。如果 base 的值介于 2 和 36(含)之间,则 主题序列的预期形式是表示一个字母和数字的序列 整数,基数由 base 指定,前面可选加号或减号, 但不包括整数后缀。从 a(或 A)到 z(或 Z)的字母是 赋予值 10 到 35;只有字母和数字的归属值较小 比基地是允许的。如果 base 的值为 16,则字符 0x 或 0X 可能 可选地在字母和数字序列之前,如果存在则在符号之后。

¶4 主题序列定义为输入字符串的最长初始子序列,...

特别是,¶3 阐明了主题序列是什么。

【讨论】:

【参考方案3】:

我觉得C99语言还是挺清晰的:

主题序列被定义为最长的初始子序列 输入字符串,从第一个非空白字符开始, 这是预期的形式。

给定"0x@""0x@" 不是预期的形式; "0x" 不是预期的形式;因此"0" 是预期形式的最长初始子序列。

我同意这意味着您不能有不符合预期形式的非空主题序列 - 除非您解释以下内容:

"C" 语言环境之外,其他特定于语言环境的主题 可以接受序列形式。

...允许语言环境定义主题序列可能具有的其他可能形式,但这些形式并非“预期形式”。

最后一段中的措辞似乎只是“腰带和大括号”。

【讨论】:

我很感兴趣,你认为人们可以如何使用 locale 子句绕过这个要求.. :-)【参考方案4】:

POSIX spec for strtol 好像更清楚了:

这些函数应将 str 指向的字符串的初始部分分别转换为 long 类型和 long long 表示形式。首先,他们将输入字符串分解为三部分:

    一个初始的,可能为空的空白字符序列(由 isspace() 指定)

    一个主题序列被解释为一个整数,表示为由 base 的值确定的某个基数

    一个或多个无法识别字符的最终字符串,包括输入字符串的终止 NUL 字符。

然后他们将尝试将主题序列转换为整数,并返回结果。

但当然,它不是规范性的并且“遵循 ISO C 标准”。

【讨论】:

POSIX 的哪个版本?也许他们澄清了措辞? 2008,与您关联的同一个。 哦……对 :-)。在这种情况下,我想知道 ISO 规范是如何措辞的……绝对是奇怪的措辞。我认为意图是“跳过空白,然后尝试读取一个整数,如果失败,什么也不做” 好吧,唯一真正的极端情况是读取"0x",然后是垃圾,当基数为0或16时-它应该读为0,endptr指向x,还是无效输入? scanf 明确指定将其读取为无效输入,但 glibc 的 scanf 将其读取为 0,同时 消耗 x...顺便说一句,strtod 有更多极端情况因为有更多的字符串在截短时格式正确,中间长度格式不正确,如果完整截取则格式正确 - 例如,"1.0e+10" 格式正确,"1.0" 格式正确格式正确,但 "1.0e+" 不是。 @R..:我同意——scanf() 系列函数在需要推回多个字符的情况下会产生额外的失败;然而,已知许多 libc 实现已被破坏 - 请参阅 ***.com/questions/1425730/…

以上是关于strtol 等人的规范中的混淆语言的主要内容,如果未能解决你的问题,请参考以下文章

R语言中的一类分类。生成混淆矩阵时我做错了啥?

混淆 C++ 语言中的字符串重复

java编码规范及优化总结

c语言strtod()函数的用法

卷积网络中的几何学你了解多少?

R语言加载UCI糖尿病数据集并启动Rattle GUI调用party包中的ctree函数构建条件推理树模型Rattle混淆矩阵使用R自定义编写函数通过混淆矩阵计算特异度敏感度PPVNPV