在我的实验中这个表达
double d = strtod("3ex", &end);
使用d
初始化3.0
,并将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'
结尾。这看起来很奇怪有两个原因。
首先,由于语言规范在描述strtod
格式说明符的行为时引用%f
,我直观地期望%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
并成功转换如"3e"
?
通过查看我的实验结果,我可能会"逆向工程" sscanf
的行为:消费尽可能多"看起来正确"从不退后,然后将消耗的序列传递给strtod
。这样'e'
被%lf
消耗,然后被strtod
忽略。但究竟是语言规范中的所有内容吗?
答案 0 :(得分:1)
我只是在die.net
上找到以下说明strtod(),strtof()和strtold()函数转换初始值 字符串的一部分由nptr指向double,float和long 双重表示。
(字符串的初始部分)的预期形式是可选的 由isspace(3)识别的前导空格,可选加 ('+')或减号(' - ')然后是(i)十进制数,或 (ii)十六进制数,或(iii)无穷大,或(iv)NAN (未非数值)。
十进制数由非空的十进制数字序列组成 可能包含基数字符(小数点, 依赖于语言环境,通常为'。'),可选地后跟小数 指数。十进制指数由'E'或'e'组成,后跟一个 可选的加号或减号,后跟非空序列 十进制数字,表示乘以10的幂。
十六进制数由“0x”或“0X”后跟非空来组成 可能包含基数字符的十六进制数字序列, 可选地后跟二进制指数。二进制指数包含 然后是“P”或“p”,接着是可选的加号或减号 通过非空的十进制数字序列,并指示乘法 基数为2.基数字符和二进制指数中的至少一个 必须在场。
无穷大是“INF”或“INFINITY”,无视案例。
NAN是“NAN”(无视案例),可选地后跟'(',a 字符序列,后跟')'。字符串 以依赖于实现的方式指定NAN的类型。
然后我进行了一项实验,我用gcc
执行了下面的代码#include <stdlib.h>
#include <stdio.h>
char head[1024], *tail;
void core(const char *stmt){
sprintf(head, "%s", stmt);
double d=strtod(head, &tail);
printf("cover %s to %.2f with length=%ld.\n", head, d, tail-head);
}
int main(){
core("3.0x");
core("3e");
core("3ex");
core("3e0x");
return 0;
}
并获得结果
cover 3.0x to 3.00 with length=3.
cover 3e to 3.00 with length=1.
cover 3ex to 3.00 with length=1.
cover 3e0x to 3.00 with length=3.
所以,似乎'e'后面应该有一些数字。
对于sscanf
,我使用gcc代码进行了另一项实验:
#include <stdlib.h>
#include <stdio.h>
char head[1024];
void core(const char *stmt){
int i;sscanf(stmt, "%x%s", &i, head);
printf("sscanf %s catch %d with '%s'.\n", stmt, i, head);
}
int main(){
core("0");
core("0x0g");
core("0x1g");
core("0xg");
return 0;
}
然后得到以下输出:
sscanf 0 catch 0 with ''.
sscanf 0x0g catch 0 with 'g'.
sscanf 0x1g catch 1 with 'g'.
sscanf 0xg catch 0 with 'g'.
似乎sscanf会尝试捕获更多的字符,如果它判断它是法律上的,则不会反击(可能不完整的情况)。