アンダースコアで始まる名前は実装用に予約されていることに注意してください。コード内でそのような名前を使用しないことをお勧めします。したがって、_val
ちょうどあるべきですval
。
およびその関係者のエラー処理の完全な仕様strtol()
は複雑で、最初に遭遇したときは驚くほど複雑です。あなたが完全に正しいことの 1 つは、関数を使用して呼び出すことstrtol()
です。コードで「そのまま」使用することは、おそらく正しくありません。
質問は C と C++ の両方でタグ付けされているため、C2011 標準から引用します。C++ 標準の適切な表現を自分で見つけることができます。
ISO/IEC 9899:2011 §7.22.1.4 strtol
、strtoll
、strtoul
およびstrtoull
関数
long int strtol(const char * restrict nptr, char ** restrict endptr, int base);
¶2 [...] まず、入力文字列を 3 つの部分に分解します。最初の、おそらく空の空白文字のシーケンス (isspace 関数で指定)、特定の基数で表される整数に似たサブジェクト シーケンスです。 base の値と、入力文字列の終端の null 文字を含む、認識されない 1 つ以上の文字の最終文字列。[...]
¶7 サブジェクト シーケンスが空であるか、期待される形式を持たない場合、変換は実行されません。の値は、nptr
が指すオブジェクトに格納されます。endptr
ただし、endptr
はヌル ポインタではありません。
戻り値
¶8 strtol
、strtoll
、strtoul
、およびstrtoull
関数は、変換された値があればそれを返します。変換を実行できなかった場合は、0 が返されます。正しい値が表現可能な値の範囲外にある場合、LONG_MIN、LONG_MAX、LLONG_MIN、LLONG_MAX、ULONG_MAX、または ULLONG_MAX が返され (戻り値の型と値の符号があれば)、マクロ ERANGE の値は次のようになります。に格納されerrno
ます。
標準 C ライブラリ関数はerrno
0 に設定されないことに注意してください。したがって、信頼性を高めるには、 をerrno
呼び出す前に 0 に設定する必要がありますstrtol()
。
したがって、parseLong()
関数は次のようになります。
static long parseLong(const char *str)
{
errno = 0;
char *temp;
long val = strtol(str, &temp, 0);
if (temp == str || *temp != '\0' ||
((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
fprintf(stderr, "Could not convert '%s' to long and leftover string is: '%s'\n",
str, temp);
// cerr << "Could not convert '" << str << "' to long and leftover string is '"
// << temp << "'\n";
return val;
}
エラーの場合、返された内容に応じて、0 または LONG_MIN または LONG_MAX が返されることに注意してくださいstrtol()
。呼び出しコードが変換が成功したかどうかを知る必要がある場合は、別の関数インターフェイスが必要です — 以下を参照してください。また、エラーはstderr
ではなくstdout
に出力する必要があり、エラー メッセージは改行で終了する必要があることに注意してください\n
。そうでない場合、タイムリーに表示される保証はありません。
さて、ライブラリ コードではおそらく印刷は必要なく、呼び出し元のコードは変換が成功したか失敗したかを知りたがるかもしれないので、インターフェースも修正するかもしれません。その場合、おそらく関数を変更して、成功/失敗の表示を返すようにします。
bool parseLong(const char *str, long *val)
{
char *temp;
bool rc = true;
errno = 0;
*val = strtol(str, &temp, 0);
if (temp == str || *temp != '\0' ||
((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE))
rc = false;
return rc;
}
次のように使用できます。
if (parseLong(str, &value))
…conversion successful…
else
…handle error…
「末尾のジャンク」、「無効な数値文字列」、「値が大きすぎる」、「値が小さすぎる」(および「エラーなし」) を区別する必要がある場合enum
は、ブール値の戻りコードの代わりに整数 or を使用します。末尾の空白は許可するが他の文字は許可しない場合、または先頭の空白を許可したくない場合は、関数でさらに作業を行う必要があります。このコードでは、8 進数、10 進数、および 16 進数を使用できます。厳密に 10 進数が必要な場合は、への呼び出しで 0 を 10 に変更する必要がありますstrtol()
。
関数が標準ライブラリの一部としてマスカレードする場合、それらは永続的に設定されるべきではないerrno
ため0
、コードをラップして保存する必要がありますerrno
。
int saved = errno; // At the start, before errno = 0;
…rest of function…
if (errno == 0) // Before the return
errno = saved;