【发布时间】:2014-12-07 16:30:43
【问题描述】:
在我的实验中这个表达式
double d = strtod("3ex", &end);
用3.0 初始化d,并将end 指针放在输入字符串中的'e' 字符处。这正是我期望它的行为。 'e' 字符可能看起来是指数部分的开头,但由于缺少实际的指数值(6.4.4.2 要求),因此 'e' 应被视为完全独立的字符。
但是,当我这样做时
double d;
char c;
sscanf("3ex", "%lf%c", &d, &c);
我注意到sscanf 使用'3' 和'e' 的%lf 格式说明符。变量d 接收3.0 值。变量c 以'x' 结束。这对我来说看起来很奇怪,原因有两个。
首先,由于语言规范在描述%f 格式说明符的行为时引用了strtod,我直观地期望%lf 以与strtod 相同的方式处理输入(即选择与终止点)。但是,我知道从历史上看,scanf 应该不超过一个字符返回输入流。这限制了任何前瞻scanf 可以由一个字符执行的距离。上面的例子需要至少两个字符的前瞻。所以,假设我接受 %lf 消耗了输入流中的 '3' 和 'e' 的事实。
但是我们遇到了第二个问题。现在sscanf 必须将"3e" 转换为类型double。 "3e" 不是浮点常量的有效表示(同样,根据 6.4.4.2,指数值不是可选的)。我希望sscanf 将此输入视为错误:在%lf 转换期间终止,返回0 并保持d 和c 不变。但是,上述sscanf 成功完成(返回2)。
这种行为在标准库的 GCC 和 MSVC 实现之间是一致的。
所以,我的问题是,在 C 语言标准文档中,它究竟允许sscanf 的行为如上所述,参考以上两点:消耗超过strtod 并成功转换为@ 等序列987654360@?
通过查看我的实验结果,我可能可以“逆向工程”sscanf 的行为:消耗尽可能多的“看起来正确”,从不后退,然后将消耗的序列传递给 strtod。这样'e' 被%lf 消耗,然后被strtod 忽略。但是语言规范中的所有内容都是这样吗?
【问题讨论】:
-
@HighPredator: OP 可能意味着变量
c应该达到值'e'而不是值'x'。或者它根本不应该获得任何值,并且函数sscanf应该返回 1 而不是 2(因此它准确地模拟了strtod的行为)。 -
@HighPredator:我实际上描述了我在问题中遇到的两个问题。我一直直观地期望
sscanf格式要求和行为与strto...格式要求和行为同步。语言标准实际上说明了这一点,但显然我在其中看到了更多的东西。例如,我预计sscanf会在strto...停止的同一点停止。现在我有点“看到”标准可能不需要这样做,并允许sscanf消耗更多。 -
虽然您观察到的行为看起来有点奇怪,但
sscanf和strtod并不要求表现出相似(或等效)的行为。strto.*scanf()需要从左到右扫描。但是strtod()可能会“向前看”并决定将 endptr 放在哪里。 -
@Blue Moon:是的,但是语言规范通过简单地引用
strtod来定义f格式说明符的行为。如果f说明符和strtod之间存在差异,则标准应在某处对其进行描述。我的问题是:在哪里?具体是哪个措辞? -
一个有趣的重复案例——与其说是 question,不如说是 answer:Difference between scanf() and strtol() / strtod() in parsing numbers 基本上,
...scanf()被定义为取最长的可能序列,即或者是匹配输入的前缀,而strto...()取最长的有效序列。 (区别在于流只支持一个保证回退的字符,即...scanf()不能像strto...()那样回退。)
标签: c scanf standard-library strtod