`strtod(“3ex”,&end)应该是什么结果? 怎么样`sscanf`?

在我的实验中这个表达

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消耗%lf格式说明符的'3''e' 。 变量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并保持dc不变。 但是,上面的sscanf成功完成(返回2 )。

这种行为在标准库的GCC和MSVC实现之间是一致的。

所以,我的问题是,在C语言标准文档中它确实允许sscanf如上所述行为,参考上述两点:消耗比strtod更多的function并成功地将这样的序列转换为"3e"

通过查看我的实验结果,我可以“反向工程” sscanf的行为:消耗尽可能多的“看起来正确”从不退后,然后将消耗的序列传递给strtod 。 那种'e'%lf消耗然后被strtod忽略的方式。 但究竟是语言规范中的所有内容吗?

我只是在die.net上找到以下描述

strtod(),strtof()和strtold()函数分别将nptr指向的字符串的初始部分转换为double,float和long double表示。

(字符串的初始部分)的预期forms是可选的前导空格,由isspace(3)识别,可选加号(’+’)或减号(’ – ‘),然后是(i)十进制数字,或(ii)hex数,或(iii)无穷大,或(iv)NAN(非数字)。

十进制数由非空的十进制数字序列组成,可能包含基数字符(小数点,依赖于语言环境,通常为’。’),可选地后跟十进制指数。 十进制指数由’E’或’e’组成,后跟可选的加号或减号,后跟非空的十进制数字序列,并表示乘以10的幂。

hex数由“0x”或“0X”组成,后跟非空的hex数字序列,可能包含基数字符,可选地后跟二进制指数。 二进制指数由’P’或’p’组成,后跟可选的加号或减号,后跟非空的十进制数字序列,并表示乘以2的幂。至少有一个基数字符和二进制指数必须在场。

无穷大是“INF”或“INFINITY”,无视案例。

NAN是“NAN”(无视大小写),可选地后跟’(’,一系列字符,后跟’)’。 字符串以依赖于实现的方式指定NAN的类型。

然后我进行了一个实验,我用gcc执行了下面的代码

 #include  #include  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  #include  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会试图捕获更多的字符,如果它判断它是法律上的,则不会反击(可能是不完整的情况)。