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

时间:2014-10-13 06:51:59

标签: c scanf standard-library strtod

在我的实验中这个表达

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,我直观地期望%lfstrtod的方式处理输入(即选择与终止点相同的位置)。但是,我知道历史上scanf应该返回不超过一个字符回输入流。这限制了任何前瞻scanf可以由一个角色执行的距离。上面的例子需要至少两个字符前瞻。所以,我们说我接受%lf从输入流中消耗了'3''e'这一事实。

但接下来我们遇到了第二个问题。现在sscanf必须将"3e"转换为double类型。 "3e"不是浮点常量的有效表示(同样,根据6.4.4.2,指数值不是可选的)。我希望sscanf将此输入视为错误:在%lf转换期间终止,返回0并保持dc不变。但是,上述sscanf成功完成(返回2)。

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

所以,我的问题是,C语言标准文档中的确切位置允许sscanf按上述方式行事,参考上述两点:消费超过strtod并成功转换如"3e"

这样的序列

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

1 个答案:

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