第2章: C++の型システムとメモリ

C++は静的型付け言語です。これは、コンパイル時にすべての変数の型が決定され、一度決まった型は変更できないことを意味します。この厳密な型システムは、一見すると面倒に感じるかもしれませんが、大規模なプログラムでもバグを未然に防ぎ、高いパフォーマンスを引き出すための重要な仕組みです。

この章では、C++の基本的な型と、他の高級言語ではあまり意識することのない「メモリ」との関係について学んでいきましょう。

基本データ型

他の多くの言語と同様に、C++にも数値を扱うための基本的なデータ型が用意されています。すでにご存知のものが多いと思いますが、C++における特徴と合わせて再確認しましょう。

型 (Type)説明 (Description)サイズの例 (Typical Size)値の範囲の例 (Example Range)
int整数を格納します (Integer)4 bytes-2,147,483,6482,147,483,647
double倍精度浮動小数点数を格納します (Double-precision float)8 bytes±1.7E308 (有効数字15桁程度)
char1文字を格納します (Character)1 byte-128127 または 0255
bool真偽値を格納します (Boolean)1 bytetrue または false

ポイント: C++の規格では、intが何バイトであるかといったサイズを厳密には定めていません。環境(OSやCPUアーキテクチャ)によって変わる可能性があります。しかし、多くのモダンな環境では上記のサイズが一般的です。

変数の宣言・代入・初期化

変数は、値を入れておくための「名前付きの箱」のようなものです。C++で変数を使うには、まず「どのような種類の箱()を、どんな名前で用意するか」をコンピュータに伝える必要があります。これを宣言 (Declaration) と呼びます。

// 整数を入れるための'age'という名前の箱を宣言 int age;

宣言した変数に値を入れることを代入 (Assignment) と言います。代入には = 記号を使います。

// 宣言済みの変数 'age' に 30 を代入 age = 30;

多くの場合、宣言と代入は同時に行います。これを初期化 (Initialization) と呼び、こちらの書き方が一般的で安全です。

data_types.cpp

基本的な演算

C++では、数値型の変数を使って基本的な算術計算ができます。

演算子意味結果
+加算5 + 27
-減算5 - 23
*乗算5 * 210
/除算5 / 22
%剰余5 % 21

⚠️ 整数除算の注意点

ここで特に注意が必要なのが / (除算) です。整数 (int) 同士の割り算の結果は、小数点以下が切り捨てられ、整数 (int) になります。

integer_division.cpp

7 / 23 になってしまうのは、int 型の aint 型の b で演算した結果もまた int 型になる、というC++のルールのためです。小数点以下の値を得たい場合は、7.0 のように、どちらかの値を double などの浮動小数点数型にする必要があります。

型を厳密に扱う

静的型付けの恩恵を最大限に受けるために、C++には型をより安全かつ便利に扱うための仕組みがあります。

`const`による不変性の保証

const (constantの略) は、変数を読み取り専用にするためのキーワードです。一度constで初期化された変数の値は、後から変更しようとするとコンパイルエラーになります。

なぜconstが重要なのでしょうか?

  • 安全性の向上: 変更されるべきでない値を誤って変更してしまうバグを防ぎます。
  • 意図の明確化: プログラムを読む人に対して、「この値は変わらない」という意図を明確に伝えられます。

円周率のように、プログラム中で決して変わることのない値にconstを使うのが典型的な例です。

const_example.cpp

`auto`による型推論

C++11から導入されたautoキーワードを使うと、コンパイラが初期化式から変数の型を自動で推論してくれます。これにより、特に型名が長い場合にコードを簡潔に書くことができます。

// autoを使わない場合 std::vector<int>::iterator it = my_vector.begin(); // autoを使う場合 auto it = my_vector.begin(); // コンパイラが it の型を std::vector<int>::iterator と推論してくれる

ただし、autoはあくまで「型を書く手間を省く」ものであり、変数が型を持たないわけではありません(動的型付け言語とは異なります)。初期化と同時に使う必要があり、型が明確な場面で適切に使うことが推奨されます。

auto x = 10; // x は int型になる auto y = 3.14; // y は double型になる auto z = "hello"; // z は const char* (C言語スタイルの文字列) になるので注意

C++の文字列: `std::string`

C言語では文字列をcharの配列(char*)として扱いましたが、これは扱いにくく、バグの温床でした。モダンC++では、std::string クラスを使うのが標準的です。std::stringは、文字列の連結、長さの取得、部分文字列の取り出しといった操作を安全かつ簡単に行うための豊富な機能を提供します。

std::stringを使うには、<string>ヘッダをインクルードする必要があります。

string_example.cpp

変数とメモリ

さて、C++を深く理解する上で避けて通れないのがメモリの概念です。変数を宣言すると、コンピュータのメモリ上にその型に応じたサイズの領域が確保されます。

例えば、int x = 10; と書くと、

  1. コンパイラはint型に必要なメモリサイズ(例: 4バイト)を判断します。
  2. プログラム実行時、メモリ上のどこかに4バイトの領域が確保されます。
  3. その領域にxという名前が割り当てられ、値として10が格納(バイナリ形式で書き込み)されます。

この「メモリ上のどこか」を示すのがメモリアドレス(単にアドレスとも)です。アドレスは、メモリ上の各バイトに割り振られた通し番号のようなもので、通常は16進数で表現されます。

変数名の前に&(アドレス演算子)を付けることで、その変数が格納されているメモリアドレスを知ることができます。

memory_address.cpp

このコードを実行すると、0x7ffee... のようなアドレスが表示されるはずです(値は実行のたびに変わります)。今は「変数はメモリ上の特定の場所に、特定のサイズで存在している」というイメージを持つことが重要です。この概念は、第4章で学ぶポインタを理解するための基礎となります。

この章のまとめ

  • C++は静的型付け言語であり、コンパイル時にすべての変数の型が決まる。
    • int, double, char, bool といった基本的なデータ型が存在する。
    • 変数は宣言してから使い、宣言と同時に値を代入する初期化が一般的。
    • 基本的な四則演算ができるが、整数同士の除算は結果が整数に切り捨てられる点に注意。
    • const を使うことで、変数を不変にし、プログラムの安全性を高めることができる。
    • auto を使うことで、コンパイラに型を推論させ、コードを簡潔に書くことができる。
    • モダンC++では、文字列は**std::string** クラスを使って安全かつ便利に扱う。
    • 宣言された変数は、メモリ上の特定のアドレスに、その型に応じたサイズの領域を確保して格納される。

練習問題1

あなたの名前(std::string)、年齢(int)、視力(double)をそれぞれ変数として宣言し、コンソールに出力するプログラムを書いてください。ただし、名前は一度決めたら変わらないものとして、constを使って宣言してください。

practice2_1.cpp

練習問題2

2つのstd::string変数 firstNamelastName を宣言し、あなたの姓名で初期化してください。その後、これら2つの変数を連結してフルネームをfullNameという新しい変数に格納し、そのフルネームと文字数(長さ)をコンソールに出力するプログラムを書いてください。

practice2_2.cpp