web-dev-qa-db-ja.com

int8_tとuint8_tはchar型を意図していますか?

このC++ 11プログラムが与えられた場合、数字または文字が表示されるはずです。または期待しないでください?

#include <cstdint>
#include <iostream>

int main()
{
    int8_t i = 65;
    std::cout << i;
}

標準では、このタイプが文字タイプであるかどうかを指定していますか?

57
Drew Dormann

C++ 0x FDIS(N3290)の18.4.1 [cstdint.syn]から、_int8_t_は次のように指定されるオプションのtypedefです。

_namespace std {
  typedef signed integer type int8_t;  // optional
  //...
} // namespace std
_

§3.9.1 [basic.fundamental]の状態:

5つの標準符号付き整数型があります:「_signed char_」、「_short int_」、「int」 、「_long int_」、「_long long int_」。このリストでは、各タイプは少なくともリスト内でその前にあるストレージと同じくらいのストレージを提供します。実装定義拡張符号付き整数型もあります。標準および拡張符号付き整数型は、符号付き整数型と総称されます。

...

タイプboolchar、_char16_t_、_char32_t_、_wchar_t_、および符号付きおよび符号なし整数型はまとめて呼び出されます整数型。整数型の同義語は、整数型です。

§3.9.1は次のようにも述べています。

特定の実装では、プレーンなcharオブジェクトは_signed char_または_unsigned char_と同じ値を取ることができます。どちらが実装定義です。

_int8_t_は、charオブジェクトが符号付きの値を取る場合、charのtypedefであると結論付けるのは魅力的です。ただし、char符号付き整数型(標準および場合によっては拡張符号付き整数型)のリストに含まれないため、これは当てはまりません。 Stephan T. Lavavejのコメント on _std::make_unsigned_および_std::make_signed_も参照してください。

したがって、_int8_t_は_signed char_のtypedefであるか、オブジェクトがちょうど8ビットのストレージを占有する拡張符号付き整数型です。

ただし、あなたの質問に答えるために、仮定をするべきではありません。 x.operator<<(y)operator<<(x,y)の両方の形式の関数が定義されているため、13.5.3 [over.binary]では、13.3.1.2 [over.match.oper]を参照して_std::cout << i_の解釈。 §13.3.1.2は、実装が§13.3.2および§13.3.3に従って候補関数のセットから選択することを示しています。次に、13.3.3.2 [over.ics.rank]を参照して以下を決定します。

  • _int8_t_が_signed char_の完全一致(つまり、_signed char_のtypedef)の場合、template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, signed char)テンプレートが呼び出されます。
  • そうでなければ、_int8_t_がintに昇格され、basic_ostream<charT,traits>& operator<<(int n)メンバー関数が呼び出されます。

uの_std::cout << u_の場合_uint8_t_オブジェクト:

  • _uint8_t_が_unsigned char_に完全一致する場合、template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, unsigned char)テンプレートが呼び出されます。
  • それ以外の場合、intはすべての_uint8_t_値を表すことができるため、_uint8_t_はintに昇格され、basic_ostream<charT,traits>& operator<<(int n)メンバー関数が呼び出されます。

常に文字を印刷したい場合、最も安全で最も明確なオプションは次のとおりです。

_std::cout << static_cast<signed char>(i);
_

そして、常に数字を印刷したい場合:

_std::cout << static_cast<int>(i);
_
25
Daniel Trebbien

_int8_t_は正確に8ビット幅です(存在する場合)。

8ビットにすることができる定義済みの整数型は、char、_unsigned char_、および_signed char_のみです。 shortと_unsigned short_の両方が少なくとも16ビットである必要があります。

したがって、_int8_t_は、_signed char_またはプレーンchar(プレーンcharが署名されている場合は後者)のいずれかのtypedefでなければなりません。

_int8_t_値を文字としてではなく整数として出力する場合、intに明示的に変換できます。

原則として、C++コンパイラは、8ビット拡張整数型(おそらく___int8_のようなものと呼ばれる)を定義し、_int8_t_そのtypedef。私がそうすることを考えることができる唯一の理由は、_int8_t_を文字型にすることを避けるためです。実際にこれを行ったC++コンパイラは知りません。

_int8_t_と拡張整数型の両方がC99で導入されました。 Cの場合、char型が使用可能なときに8ビット拡張整数型を定義する特別な理由はありません。

[〜#〜] update [〜#〜]

私はこの結論に完全には満足していません。 _int8_t_および_uint8_t_はC99で導入されました。 Cでは、文字タイプかどうかは特に関係ありません。区別が実際の違いを生む操作はありません。 (標準Cの最低レベルの文字出力ルーチンであるputc()でさえ、int引数として印刷される文字を取ります)。 _int8_t_、および_uint8_t_は、定義されている場合、ほぼ確実に文字型として定義されますが、文字型は単なる小さな整数型です。

C++は、char、_operator<<_、および_signed char_の_unsigned char_の特定のオーバーロードされたバージョンを提供するため、_std::cout << 'A'_と_std::cout << 65_は非常に異なる出力を生成します。後で、C++は_int8_t_と_uint8_t_を採用しましたが、Cの場合と同様に、それらはほぼ確実に文字型です。ほとんどの操作では、これはCの場合よりも重要ではありませんが、_std::cout << ..._の場合は違いがあります。

_uint8_t x = 65;
std::cout << x;
_

おそらく、番号_65_ではなく、文字Aを出力します。

一貫した動作が必要な場合は、キャストを追加します。

_uint8_t x = 65;
std::cout << int(x); // or static_cast<int>(x) if you prefer
_

問題の根本は、言語に欠けているものがあることだと思います。文字型ではない非常に狭い整数型です。

意図については、委員会のメンバーが問題について考えなかったか、対処する価値がないと判断したと推測できます。 _[u]int*_t_型を標準に追加する利点は、_std::cout << ..._を使用したかなり奇妙な動作の不便さを上回ると主張することができます(私もそう思います)。

23
Keith Thompson

あなたの質問に逆順に答えます。

標準では、このタイプが文字タイプであるかどうかを指定していますか?

短い回答:最も一般的なプラットフォームでは_int8_t_は_signed char_です(LinuxではGCC/Intel/Clang、WindowsではVisual Studio )しかし、他の人には何か他のものであるかもしれません。

長い答えが続きます。

C++ 11標準のセクション18.4.1は、以下を含む_<cstdint>_の概要を提供します

typedef符号付き整数型 _int8_t; //optional_

後の同じセクションの段落2では、

ヘッダー[_<cstdint>_]は、C標準の7.18と同じすべての関数、型、およびマクロを定義します。

c標準は1.1/2によるC99を意味します:

C++は、ISO/IEC 9899:1999プログラミング言語— C(以下で参照)で説明されているCプログラミング言語に基づいた汎用プログラミング言語です。 C標準として)。

したがって、_int8_t_の定義は、C99標準のセクション7.18にあります。より正確には、C99のセクション7.18.1.1には

typedef name _intN_t_は、幅N、パディングビットなし、2の補数表現を持つ符号付き整数型を指定します。したがって、int8_tは、正確に8ビットの幅を持つ符号付き整数型を示します

さらに、C99のセクション6.2.5/4には、

5つの標準符号付き整数型があり、signed charshort intintlong int、およびlong long int。 (これらの型および他の型は、6.7.2で説明されているように、いくつかの追加の方法で指定できます。)実装定義の拡張符号付き整数型もあります標準および拡張符号付き整数型は、符号付き整数型と総称されます

最後に、C99のセクション5.2.4.2.1は、標準の符号付き整数型に最小サイズを課しています。 _signed char_を除き、他のすべては少なくとも16ビット長です。

したがって、_int8_t_は_signed char_または8ビット長の拡張(非標準)符号付き整数型のいずれかです。

Glibc(GNU Cライブラリ)とVisual Studio Cライブラリは両方とも_int8_t_を_signed char_として定義します。少なくともLinuxではIntelとClangもlibcを使用するため、したがって、最も一般的なプラットフォームでは、_int8_t_は_signed char_です。

このC++ 11プログラムが与えられた場合、数字または文字が表示されるはずです。または期待しないでください?

短い答え:最も人気のあるプラットフォーム(LinuxではGCC/Intel/Clang、WindowsではVisual Studio)では、文字「A」が必ず表示されます。ただし、他のプラットフォームでは_65_が表示される場合があります。 (これを指摘してくれて、 DyP に感謝します。)

続編では、すべての参照はC++ 11標準(現在のドラフト、N3485)を参照しています。

セクション27.4.1は_<iostream>_の概要を提供します。特に、coutの宣言を示します。

_extern ostream cout;
_

現在、ostreamは、セクション27.7.1による_basic_ostream_のテンプレート特化のtypedefです。

_template <class charT, class traits = char_traits<charT> >
class basic_ostream;

typedef basic_ostream<char> ostream;
_

セクション27.7.3.6.4は、次の宣言を提供します。

_template<class traits>
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, signed char c);
_

_int8_t_が_signed char_の場合、呼び出されるのはこのオーバーロードです。また、同じセクションでは、この呼び出しの結果が文字(数字ではなく)を印刷することを指定しています。

次に、_int8_t_が拡張符号付き整数型である場合を考えてみましょう。明らかに、標準は非標準型に対してoperator<<()のオーバーロードを指定していませんが、プロモーションと変換のおかげで、提供されたオーバーロードの1つが呼び出しを受け入れる可能性があります。実際、intは少なくとも16ビット長で、_int8_t_のすべての値を表すことができます。次に、4.5/1は_int8_t_をpromoted to intにできることを示します。一方、4.7/1および4.7/2では、_int8_t_をconverted to _signed char_にできることが示されています。最後に、13.3.3.1.1は、オーバーロード解決中の変換よりもプロモーションが優先されることを示しています。したがって、次のオーバーロード(23.7.3.1で宣言)

basic_ostream&basic_ostream :: operator <<(int n);

と呼ばれます。これは、このコード

_int8_t i = 65;
std::cout << i;
_

_65_を出力します。

更新:

1DyP のコメントに続く投稿を修正しました。

2。 _int8_t_がtypedefcharになる可能性に関する次のコメントを追加しました。

前述のように、C99標準(上記のセクション6.2.5/4)は5つの標準符号付き整数型を定義し(charはそれらの1つではありません)、実装が非標準符号付き整数と呼ばれるonwを追加できるようにしますタイプ。 C++標準は、セクション3.9.1/2のその定義を強化します。

「signed char」、「short int」、「int」、「long int」、「long long int」の5つの標準符号付き整数型があります[...]実装定義の拡張符号付き整数型もあります。標準および拡張符号付き整数型は、符号付き整数型と総称されます。

後で、同じセクションで、パラグラフ7に次のように記載されています。

タイプboolchar、_char16_t_、_char32_t_、_wchar_t_、および符号付きおよび符号なし整数型統合型と総称されます。整数型の同義語は、整数型です。

したがって、charは整数型ですが、charは符号付き整数型でも符号なし整数型でもありませんおよびセクション18.4.1(上記で引用)は、_int8_t_が存在する場合、符号付き整数型のtypedefであることを示しています。

紛らわしいのは、実装によっては、charが_signed char_と同じ値をとることがあることです。特に、charには符号が付いている場合がありますが、_signed char_ではありません。これは、セクション3.9.1/1で明示的に述べられています。

[...]プレーンchar、_signed char_、および_unsigned char_は、3つの異なるタイプです。 [...]特定の実装では、プレーンcharオブジェクトは_signed char_または_unsigned char_と同じ値を取ることができます。どちらが実装定義です。

これは、charnot3.9.1/2で定義されている符号付き整数型であることも意味します。

3。私の解釈と、具体的には、「charは符号付き整数型でも符号なし整数型でもない」という文は少し議論の余地があることを認めます。

私の主張を強めるために、ステファン・T・ラヴァヴェージが同じことを言ったことを付け加えたいと思います hereJohannes Schaub-litb もコメントで同じ文を使用しました- this post。

6
Cassio Neri

私が持っている作業ドラフトのコピー、N3376では、[cstdint.syn]§18.4.1で、int型は通常typedefであると指定されています。

namespace std {
typedef signed integer type int8_t; // optional
typedef signed integer type int16_t; // optional
typedef signed integer type int32_t; // optional
typedef signed integer type int64_t; // optional
typedef signed integer type int_fast8_t;
typedef signed integer type int_fast16_t;
typedef signed integer type int_fast32_t;
typedef signed integer type int_fast64_t;
typedef signed integer type int_least8_t;
typedef signed integer type int_least16_t;
typedef signed integer type int_least32_t;
typedef signed integer type int_least64_t;
typedef signed integer type intmax_t;
typedef signed integer type intptr_t; // optional
typedef unsigned integer type uint8_t; // optional
typedef unsigned integer type uint16_t; // optional
typedef unsigned integer type uint32_t; // optional
typedef unsigned integer type uint64_t; // optional
typedef unsigned integer type uint_fast8_t;
typedef unsigned integer type uint_fast16_t;
typedef unsigned integer type uint_fast32_t;
typedef unsigned integer type uint_fast64_t;
typedef unsigned integer type uint_least8_t;
typedef unsigned integer type uint_least16_t;
typedef unsigned integer type uint_least32_t;
typedef unsigned integer type uint_least64_t;
typedef unsigned integer type uintmax_t;
typedef unsigned integer type uintptr_t; // optional
} // namespace std

唯一の要件は8ビットでなければならないため、charに対するtypedefは許容されます。

5
Rapptz