10

私の実験では、この表現

double d = strtod("3ex", &end);

で初期化dし、入力文字列内の文字にポインターを3.0置きます。これはまさに私が期待する動作と同じです。文字は指数部の先頭に見えるかもしれませんが、実際の指数値 (6.4.4.2 で必要) がないため、完全に独立した文字として扱う必要があります。end'e''e''e'

しかし、私がするとき

double d;
char c;
sscanf("3ex", "%lf%c", &d, &c);

フォーマット指定子のとのsscanf両方を消費していることに気付きました。変数は値を受け取ります。変数はその中で終わります。これは、2 つの理由から私には奇妙に見えます。'3''e'%lfd3.0c'x'

まず、言語仕様は書式指定子strtodの振る舞いを記述するときに参照するので、%f直感的%lfに入力を同じように扱うstrtod(つまり、終点と同じ位置を選ぶ) ことを期待しました。scanfただし、歴史的には、入力ストリームに返される文字は 1 つだけであると想定されていたことを知っています。scanfこれにより、1 文字で実行できる先読みの距離が制限されます。上記の例では、少なくとも 2 文字の先読みが必要です。したがって、入力ストリームからと の両方を%lf消費したという事実を受け入れるとしましょう。'3''e'

しかし、次に 2 番目の問題に直面します。それを typesscanfに変換する必要があります。浮動小数点定数の有効な表現ではありません (6.4.4.2 によれば、指数値はオプションではありません)。私はこの入力を間違ったものとして扱うことを期待しています:変換中に終了し、戻ってそのままにし、 変更しません。ただし、上記は正常に完了します ( を返します)。"3e"double"3e"sscanf%lf0dcsscanf2

この動作は、標準ライブラリの GCC 実装と MSVC 実装の間で一貫しています。

したがって、私の質問は、C 言語標準ドキュメントのどこでsscanf、上記の 2 つの点を参照して、上記のように動作することが許可さstrtodれている"3e"かということです。

私の実験結果を見ると、おそらくsscanfの動作を「リバース エンジニアリング」することができstrtodます。そのようにして、'e'によって消費され%lf、無視されstrtodます。しかし、言語仕様にはそれだけでしたか?

4

1 に答える 1

1

die.netで以下の説明を見つけました

strtod()、strtof()、および strtold() 関数は、nptr が指す文字列の最初の部分を、それぞれ double、float、および long double 表現に変換します。

文字列 (の最初の部分) の予期される形式は、isspace(3) によって認識されるオプションの先頭の空白、オプションのプラス ('+') またはマイナス記号 ('-')、および (i) 10 進数のいずれかです。 、または (ii) 16 進数、または (iii) 無限大、または (iv) NAN (非数)。

10 進数は、基数文字 (小数点、ロケール依存、通常は '.') を含む可能性のある空でない 10 進数のシーケンスで構成され、オプションで 10 進指数が続きます。10 進数の指数は、'E' または 'e' の後にオプションのプラスまたはマイナス記号が続き、その後に空でない 10 進数のシーケンスが続き、10 の累乗による乗算を示します。

16 進数は、"0x" または "0X" の後に、基数文字を含む可能性のある空でない 16 進数のシーケンスが続き、オプションで 2 進指数が続きます。2 進指数は、'P' または 'p' の後にオプションのプラスまたはマイナス記号が続き、その後に空でない 10 進数のシーケンスが続き、2 の累乗による乗算を示します。基数文字と 2 進指数の少なくとも 1 つ存在する必要があります。

無限大は、大文字と小文字を区別せずに、"INF" または "INFINITY" のいずれかです。

NAN は "NAN" (大文字と小文字を区別しない) であり、オプションで '('、一連の文字、その後に ')' が続きます。文字列は、実装依存の方法で 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 は、より多くの文字をキャッチしようとし、現在合法であると判断された場合はロールバックしないようです (不完全な状況では違法である可能性があります)。

于 2014-10-15T09:09:21.073 に答える