10

簡単なテスト プログラムがあります (エラー チェックは削除されています)。

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>

int main() {
    std::string line;
    while(std::cin >> line) {
        int value;
        std::stringstream stream(line);

        stream >> std::setbase(0) >> value;

        std::cout << "You typed: " << value << std::endl;
    }

}

これは、プレフィックスに依存する整数の解析に最適です。"0x"または"0X"で始まる文字列を 16 進数で、文字列で始まる文字列を 8 進数で解析します'0'。これは、私が使用して見たいくつかのリソースで説明されています。私が見つけることができなかったのは、これが機能することが保証されていることを C++ 標準で示していることです。

C 標準のセクション 7.20.1.4.3 にstrtolは、(6.4.4.1 は整数定数の構文です) と書かれています。

base の値が 0 の場合、サブジェクト シーケンスの期待される形式は、6.4.4.1 で説明されている整数定数の形式であり、オプションでプラス記号またはマイナス記号が前に付いていますが、整数の接尾辞は含まれていません。

これは、私が試したいくつかのバージョンの GCC で動作しますが、一般的に安全に使用できますか?

4

3 に答える 3

6

setbaseC++98 [lib.std.manip]/5 で定義されています。

smanip setbase(int base);

戻り値:s [ストリームへの挿入またはストリームからの抽出sは、あたかも次の関数がそのストリームで呼び出されたかのように動作します: ]

ios_base& f(ios_base& str, int base)
{
    str.setf(n == 8 ? ios_base::oct :
             n == 10 ? ios_base::dec :
             n == 16 ? ios_base::hex :
             ios_base::fmtflags(0), ios_base::basefield);
    return str;
}

8、10、16 のbaseいずれでもない場合、basefieldフラグはクリアされます。入力がクリアされた場合の効果は、[lib.facet.num.get.virtuals] の表 55 (「整数変換」) で定義されており、次に使用可能な文字のシーケンスに対するbasefieldものと同等です。sscanf("%i")

C++98 は*scanf、当然のことながら、の定義について C89 を参照します。私は C89 の PDF コピーを持っていませんが、セクション 7.19.6.2 パラグラフ 12 [C 標準には、C++ 標準にあるような適切なシンボリック セクション名がありません] で定義"%i"されている C99 はstrtol持っています。基本引数 0。

したがって、良いニュースは、接頭辞に依存する整数スキャンが標準 after によって保証されていることsetbase(0)です。悪いニュースは、iostream 形式の入力が で定義されていることです*scanf。これは、C99 7.19.6.2p10 の最後の恐ろしい文が適用されることを意味します。

[スキャンの結果を受け取るオブジェクト] が適切な型を持たない場合、または変換の結果をオブジェクトで表現できない場合動作は未定義です。

(私のものを強調してください。)その文のより明確なバージョン:入力オーバーフローは未定義の動作を引き起こします。入力の桁数が多すぎる場合、 C(++) ランタイムはプログラムをクラッシュさせることができます! *scanfこれが (いくつかの理由の 1 つです) 私や他の人*scanfが決して使用すべきではないと言い続けている理由istream >> intです。:-(

C に当てはまるアドバイスは、C++ に適用するのがさらに簡単です。行全体を読み取りstd::getline、手動で解析します。一連の関数を使用してstrtol、数値入力を機械数に変換します。(これらの関数は、オーバーフロー時に予測可能な動作をします。)

于 2012-11-02T14:27:26.453 に答える
3

§22.4.2.1.2/3、表 85:

整数型への変換の場合、この関数は、表 85 に示すように整数変換指定子を決定します。表は順序付けられています。つまり、条件が true である最初の行が適用されます。

Table 85 — Integer conversions
State                    stdio equivalent
basefield == oct         %o
basefield == hex         %X
basefield == 0           %i
signed integral type     %d
unsigned integral type   %u

and companyの%i変換形式はscanf、プレフィックス依存の変換を行います。

于 2012-11-02T14:24:47.467 に答える
2

§27.6.3、「標準マニピュレータ」、¶5、「」から始めましょうsmanip setbase(int base)

戻り値:が (のインスタンス) である場合、式が呼び出されたかのように動作するs、未指定の型のオブジェクト。次のように定義できます。inbasic_istreamin>>sf(s)f

ios_base& f(ios_base& str, int base)
{
  // set basefield
  str.setf(base == 8 ? ios_base::oct :
    base == 10 ? ios_base::dec :
    base == 16 ? ios_base::hex :
    ios_base::fmtflags(0), ios_base::basefield);
  return str;
}

ios_base fmtflags§27.4.2.2状態関数、¶6で探求を続けます。fmtflags setf(fmtflags fmtfl, fmtflags mask);

効果: でクリアmask、でflags()セット。fmtfl & maskflags()

0&basefieldでは、 に設定することの効果は何flags()ですか?

§27.6.1.2.2 算術エクストラクタを検討してください。これには、とりわけ次のことが記載されていますoperator>>(int& val);

これらのエクストラクタは、ロケールの num_get<> (22.2.2.1) オブジェクトに依存して、入力ストリーム データの解析を実行します。

§22.2.2.1、¶4、表 55 は、この場合に選択される変換指定子を説明しています。

basefield == 0, `%i`

最後に、¶11 は次のように述べています。

一連の文字 ... は ( の規則に従って scanf) val 型の値に変換されます。


したがって、2003 年の C++ 標準では、std::cin >> setbase(0) >> iは と同等であると述べられていscanf(..., "%i", &i)ます。

それが何意味するかについては、C 標準を参照する必要があります。

于 2012-11-02T14:30:01.900 に答える