C++でプログラミングを行う際、データ(数値や文字など)を格納するためには、そのデータの種類に応じた「型」を指定する必要があります。型は、コンパイラに対して、そのデータのためにどれだけのメモリ領域を確保すべきか、そしてそのビットパターンをどのように解釈すべきかを指示する設計図のようなものです。
この記事では、C++の最も基本的な「算術型」である整数型、浮動小数点型、bool型について詳しく解説します。
汎整数型(Integral Types)
汎整数型は、その名の通り、整数(小数部分を持たない数)を扱うためのデータ型です。C++の整数型は、そのサイズ(使用するビット数)と、負の値を扱えるかどうか(符号の有無)によって細かく分類されます。
bool: ブール型。trueまたはfalseの真偽値を保持します。char: 文字型。1バイトのデータを保持し、主に文字コード(ASCIIなど)を格納するために使われます。short(またはshort int): 短い整数型。int: 標準の整数型。処理系にとって最も効率の良いサイズが選ばれます。long(またはlong int): 長い整数型。long long(またはlong long int): (C++11以降)longよりも大きな、または同等のサイズを持つ整数型。
符号付き (signed) と 符号なし (unsigned)
char を除く上記の整数型(short, int, long, long long)は、デフォルトで signed(符号付き) となり、正の値、負の値、そして0を表現できます。
これらの型名の前に unsigned という型指定子を付けると、unsigned(符号なし) 型となり、0および正の値のみを表現できます。その代わり、表現できる正の最大値が signed 型の約2倍になります。
(char については、signed か unsigned かは処理系(コンパイラ)によって異なります。signed char, unsigned char と明示的に指定することが推奨されます。)
型の範囲:<climits>ヘッダ
C++の規格では、各整数型が最低限どれだけの範囲を表現できなければならないかは定められていますが、具体的なビット数(例:int が32ビットであること)は保証されていません。これは、実行される環境(CPUやOS)によって最適なサイズが異なるためです。
各型が現在の処理系でどれだけの値を表現できるかを知るために、<climits> というヘッダファイルが提供されています。このヘッダは、各型の最小値や最大値を定義したオブジェクト形式マクロ(定数)を含んでいます。
マクロは #define 指令によって定義され、コンパイル前にプリプロセッサによって指定された値(テキスト)に単純置換されます。これにより、コードの可読性や保守性が向上します。
コード例:整数型で表現できる値の表示
以下のコードは、<climits> ヘッダを使用して、お使いの環境での各整数型の最小値と最大値を表示します。
#include <iostream>
#include <climits> // 各種整数型の最小値・最大値の定数が定義されている
int main() {
std::cout << "--- この処理系の整数型で表現できる値 ---\n";
std::cout << "char: " << CHAR_MIN << " ~ " << CHAR_MAX << "\n";
std::cout << "signed char: " << SCHAR_MIN << " ~ " << SCHAR_MAX << "\n";
std::cout << "unsigned char: 0 ~ " << UCHAR_MAX << "\n";
std::cout << "short: " << SHRT_MIN << " ~ " << SHRT_MAX << "\n";
std::cout << "unsigned short: 0 ~ " << USHRT_MAX << "\n";
std::cout << "int: " << INT_MIN << " ~ " << INT_MAX << "\n";
std::cout << "unsigned int: 0 ~ " << UINT_MAX << "\n";
std::cout << "long: " << LONG_MIN << " ~ " << LONG_MAX << "\n";
std::cout << "unsigned long: 0 ~ " << ULONG_MAX << "\n";
// C++11以降で保証されている
std::cout << "long long: " << LLONG_MIN << " ~ " << LLONG_MAX << "\n";
std::cout << "unsigned long long: 0 ~ " << ULLONG_MAX << "\n";
std::cout << "--------------------------------------\n";
// C++では、<climits> のC言語スタイルよりも、<limits> ヘッダの
// std::numeric_limits<T> を使うことがより推奨されますが、
// <climits> も広く使われています。
return 0;
}
文字型 (char)
char 型は、1バイト(<climits> の CHAR_BIT で定義されたビット数、通常は8ビット)のサイズを持つ整数型です。
これは「整数」型ですが、多くの場合、数値そのものとしてではなく、文字コード(例: ASCIIコード)を格納するために使用されます。
文字リテラル
コード内で特定の文字を表すには、'A' のようにシングルクォート(')で文字を囲みます。これを文字リテラルと呼びます。
コード例:文字と文字コード
#include <iostream>
int main() {
char symbol_a = 'A';
// ASCIIコードで 66 は 'B' に相当
char symbol_b = 66;
std::cout << "--- 文字型 (char) の表示 ---\n";
// char型として出力すると、文字が表示される
std::cout << "symbol_a: " << symbol_a << "\n";
std::cout << "symbol_b: " << symbol_b << "\n";
// int型にキャスト(変換)すると、格納されている数値(文字コード)が表示される
std::cout << "symbol_a の文字コード: " << static_cast<int>(symbol_a) << "\n";
std::cout << "symbol_b の文字コード: " << static_cast<int>(symbol_b) << "\n";
return 0;
}
整数リテラル(数値の記述法)
コード内に直接記述する数値のことを整数リテラルと呼びます。C++では、数値を複数の基数(進数)で記述できます。
- 10進数:
123,456(接頭辞なし) - 16進数:
0x7B(0xまたは0Xで始める) - 8進数:
0173(0で始める) - 2進数:
0b01111011(C++14以降。0bまたは0Bで始める)
整数接尾語(サフィックス)
リテラルがどの型として扱われるかを明示的に指定するために、接尾語(サフィックス)を付けることができます。
Uまたはu:unsignedLまたはl:longLLまたはll:long long(C++11以降)
これらは組み合わせることができます(例: 123ULL は unsigned long long 型)。
オブジェクトと sizeof 演算子
C++においてオブジェクトとは、特定のデータ型を持つメモリ上の一領域を指します。int myVariable; と宣言した時点で、myVariable という名前の int 型オブジェクトが作成されます。
sizeof 演算子は、指定した型、または指定したオブジェクト(変数)が、メモリ上で何バイトを占有しているかを調べるために使用されます。
コード例:各種の整数型と変数の大きさを表示
#include <iostream>
int main() {
std::cout << "--- 各種整数型のサイズ (バイト単位) ---\n";
// 型名を指定
std::cout << "sizeof(char): " << sizeof(char) << "\n";
std::cout << "sizeof(short): " << sizeof(short) << "\n";
std::cout << "sizeof(int): " << sizeof(int) << "\n";
std::cout << "sizeof(long): " << sizeof(long) << "\n";
std::cout << "sizeof(long long): " << sizeof(long long) << "\n";
int myVariable = 100;
// 変数名(オブジェクト)を指定
std::cout << "sizeof(myVariable): " << sizeof(myVariable) << "\n";
return 0;
}
size_t 型と typedef 宣言
sizeof 演算子が返す値の型は、size_t という特別な型です。size_t は、処理系で表現可能な最大のオブジェクトサイズを保持できる、符号なし整数型として定義されています。
伝統的に、size_t のような型エイリアス(別名)は typedef 宣言を用いて定義されてきました。
// 伝統的な typedef による定義(例)
// 「unsigned long」という型に「size_t」という別名を与える
typedef unsigned long size_t;
(現代のC++では、using size_t = unsigned long; という using エイリアス宣言が好まれる傾向にあります。)
typeid 演算子
typeid 演算子は、指定された変数や型に関する情報を実行時に取得するために使用されます。これを使用するには <typeinfo> ヘッダをインクルードする必要があります。.name() メンバ関数を呼び出すと、その型の名前を表す文字列(処理系に依存)を取得できます。
コード例:各種の変数や定数の型情報を表示
#include <iostream>
#include <typeinfo> // typeid を使うために必要
#include <string>
int main() {
int stockQuantity = 150;
double unitPrice = 19.99;
std::string productName = "Widget";
std::cout << "--- typeid による型情報の表示 ---\n";
// 変数を指定
std::cout << "stockQuantity の型: " << typeid(stockQuantity).name() << "\n";
std::cout << "unitPrice の型: " << typeid(unitPrice).name() << "\n";
std::cout << "productName の型: " << typeid(productName).name() << "\n";
// リテラルや型そのものを指定
std::cout << "リテラル 10L の型: " << typeid(10L).name() << "\n";
std::cout << "型 'float' の名前: " << typeid(float).name() << "\n";
return 0;
}
整数の内部表現
コンピュータのメモリは、0と1のビットの集まりです。
- 符号なし整数の内部表現: 単純な2進数で表現されます。例えば8ビットの
unsigned charで5は00000101となります。 - 符号あり整数の内部表現: 多くの現代のコンピュータでは、**2の補数(Two’s Complement)**という表現形式が採用されています。これにより、正の数と負の数を効率的に(加算器のロジックを変更せずに)扱うことができます。
bool 型
bool 型は、論理的な真偽値を表現するための型です。true(真)または false(偽)の2つの値のみを取ります。内部的には整数として扱われ、true は 1、false は 0 に対応します。
コード例:bool型の値を表示
std::cout はデフォルトで bool 値を 1 または 0 として出力しますが、<iomanip> ヘッダの std::boolalpha 操作子を使用すると、true または false という文字列として出力させることができます。
#include <iostream>
#include <iomanip> // std::boolalpha, std::noboolalpha のために必要
int main() {
bool isAuthorized = true;
bool needsUpdate = false;
std::cout << "--- bool型の表示 ---\n";
// デフォルト(数値)での表示
std::cout << "デフォルト isAuthorized: " << isAuthorized << "\n";
std::cout << "デフォルト needsUpdate: " << needsUpdate << "\n";
// std::boolalpha を設定
std::cout << std::boolalpha;
std::cout << "boolalpha isAuthorized: " << isAuthorized << "\n";
std::cout << "boolalpha needsUpdate: " << needsUpdate << "\n";
// std::noboolalpha で数値表示に戻す
std::cout << std::noboolalpha;
std::cout << "noboolalpha isAuthorized: " << isAuthorized << "\n";
return 0;
}
浮動小数点型 (Floating-Point Types)
浮動小数点型は、実数(小数や非常に大きな数)を近似的に表現するために使用されます。C++には主に3つの浮動小数点型があります。
float: 単精度浮動小数点数。double: 倍精度浮動小数点数。floatよりも高い精度と広い範囲の値を表現できます。long double: 拡張精度浮動小数点数。doubleよりもさらに高い精度と範囲を持つ(場合がある)型。
浮動小数点リテラル
コード内に記述する浮動小数点リテラルは、デフォルトで double 型として扱われます。
3.14(double型)1.23e4(1.23 * 10^4。科学技術表記。double型)3.14fまたは3.14F(float型)3.14Lまたは3.14l(long double型)
コード例:浮動小数点型の精度
float は double よりもメモリ使用量が少ないですが、表現できる精度(有効桁数)が低くなります。
#include <iostream>
#include <iomanip> // std::setprecision のために必要
int main() {
// 円周率
float pi_f = 3.14159265358979323846f;
double pi_d = 3.14159265358979323846;
long double pi_ld = 3.14159265358979323846L;
std::cout << "--- 浮動小数点型の精度比較 (20桁表示設定) ---\n";
// 表示する精度を設定
std::cout << std::setprecision(20);
std::cout << "float (pi_f): " << pi_f << "\n";
std::cout << "double (pi_d): " << pi_d << "\n";
std::cout << "long double (pi_ld): " << pi_ld << "\n";
return 0;
}
このコードを実行すると、float 型が途中の桁で精度を失っているのに対し、double や long double はより正確な値を保持できていることが確認できます。
まとめ:適切な型を選ぶ重要性
この記事では、C++の最も基本的な「算術型」である整数型、浮動小数点型、bool型、文字型について解説しました。
- 整数型 (
int,long,charなど): 整数を扱います。値が負になる可能性がない場合はunsignedを指定することで、扱える正の最大値を約2倍にできます。 - 浮動小数点型 (
float,double): 小数を含む実数を扱います。最後のコード例で示した通り、floatはdoubleよりもメモリ使用量が少ない代わりに、表現できる精度(有効桁数)が大幅に低くなります。計算の正確性が求められる場合はdoubleを使用するのが一般的です。 - bool型:
trueまたはfalseの真偽値のみを保持します。 sizeof演算子は、これらの型が使用する環境でどれだけのメモリ(バイト単位)を占有するかを調べるために不可欠です。
データを扱う際は、その値が取りうる範囲(例:負の値は必要か?最大値はどれくらいか?)や、必要な精度(例:金融計算か、大まかな物理シミュレーションか?)を常に意識し、その状況に対して最も適切な型を選択することが、メモリ効率とプログラムの正確性を両立させる鍵となります。
