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】:如果您从 C99 标准的 §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语言加载UCI糖尿病数据集并启动Rattle GUI调用party包中的ctree函数构建条件推理树模型Rattle混淆矩阵使用R自定义编写函数通过混淆矩阵计算特异度敏感度PPVNPV