C++は静的型付け言語です。これは、コンパイル時にすべての変数の型が決定され、一度決まった型は変更できないことを意味します。この厳密な型システムは、一見すると面倒に感じるかもしれませんが、大規模なプログラムでもバグを未然に防ぎ、高いパフォーマンスを引き出すための重要な仕組みです。
この章では、C++の基本的な型と、他の高級言語ではあまり意識することのない「メモリ」との関係について学んでいきましょう。
他の多くの言語と同様に、C++にも数値を扱うための基本的なデータ型が用意されています。すでにご存知のものが多いと思いますが、C++における特徴と合わせて再確認しましょう。
| 型 (Type) | 説明 (Description) | サイズの例 (Typical Size) | 値の範囲の例 (Example Range) |
|---|---|---|---|
int | 整数を格納します (Integer) | 4 bytes | -2,147,483,648 ~ 2,147,483,647 |
double | 倍精度浮動小数点数を格納します (Double-precision float) | 8 bytes | 約 ±1.7E308 (有効数字15桁程度) |
char | 1文字を格納します (Character) | 1 byte | -128 ~ 127 または 0 ~ 255 |
bool | 真偽値を格納します (Boolean) | 1 byte | true または false |
ポイント: C++の規格では、intが何バイトであるかといったサイズを厳密には定めていません。環境(OSやCPUアーキテクチャ)によって変わる可能性があります。しかし、多くのモダンな環境では上記のサイズが一般的です。
変数は、値を入れておくための「名前付きの箱」のようなものです。C++で変数を使うには、まず「どのような種類の箱(型)を、どんな名前で用意するか」をコンピュータに伝える必要があります。これを宣言 (Declaration) と呼びます。
// 整数を入れるための'age'という名前の箱を宣言 int age;
宣言した変数に値を入れることを代入 (Assignment) と言います。代入には = 記号を使います。
// 宣言済みの変数 'age' に 30 を代入 age = 30;
多くの場合、宣言と代入は同時に行います。これを初期化 (Initialization) と呼び、こちらの書き方が一般的で安全です。
#include <iostream>
int main() {
// 宣言と同時に初期化
int age = 30;
double pi = 3.14159;
char initial = 'A';
bool is_student = true;
std::cout << "Age: " << age << std::endl;
std::cout << "Pi: " << pi << std::endl;
std::cout << "Initial: " << initial << std::endl;
std::cout << "Is student? " << is_student << std::endl; // boolは通常 1 (true) または 0 (false) として出力される
return 0;
}Age: 30 Pi: 3.14159 Initial: A Is student? 1
C++では、数値型の変数を使って基本的な算術計算ができます。
| 演算子 | 意味 | 例 | 結果 |
|---|---|---|---|
+ | 加算 | 5 + 2 | 7 |
- | 減算 | 5 - 2 | 3 |
* | 乗算 | 5 * 2 | 10 |
/ | 除算 | 5 / 2 | 2 |
% | 剰余 | 5 % 2 | 1 |
ここで特に注意が必要なのが / (除算) です。整数 (int) 同士の割り算の結果は、小数点以下が切り捨てられ、整数 (int) になります。
#include <iostream>
int main() {
int a = 7;
int b = 2;
std::cout << "7 / 2 = " << a / b << std::endl;
// 正しい計算結果(浮動小数点数)を得るには?
// 演算する値の少なくとも一方が浮動小数点数型である必要があります。
double c = 7.0;
std::cout << "7.0 / 2 = " << c / b << std::endl;
return 0;
}7 / 2 = 3 7.0 / 2 = 3.5
7 / 2 が 3 になってしまうのは、int 型の a と int 型の b で演算した結果もまた int 型になる、というC++のルールのためです。小数点以下の値を得たい場合は、7.0 のように、どちらかの値を double などの浮動小数点数型にする必要があります。
静的型付けの恩恵を最大限に受けるために、C++には型をより安全かつ便利に扱うための仕組みがあります。
const (constantの略) は、変数を読み取り専用にするためのキーワードです。一度constで初期化された変数の値は、後から変更しようとするとコンパイルエラーになります。
なぜconstが重要なのでしょうか?
円周率のように、プログラム中で決して変わることのない値にconstを使うのが典型的な例です。
#include <iostream>
int main() {
const double PI = 3.14159;
int radius = 5;
double area = PI * radius * radius;
std::cout << "Area: " << area << std::endl;
// PI = 3.14; // この行はコンパイルエラーになる!
return 0;
}Area: 78.5397
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言語では文字列をcharの配列(char*)として扱いましたが、これは扱いにくく、バグの温床でした。モダンC++では、std::string クラスを使うのが標準的です。std::stringは、文字列の連結、長さの取得、部分文字列の取り出しといった操作を安全かつ簡単に行うための豊富な機能を提供します。
std::stringを使うには、<string>ヘッダをインクルードする必要があります。
#include <iostream>
#include <string> // std::string を使うために必要
int main() {
// 文字列の宣言と初期化
std::string greeting = "Hello";
// 文字列の連結
std::string name = "C++";
std::string message = greeting + ", " + name + "!";
std::cout << message << std::endl;
// 文字列の長さを取得
std::cout << "Length: " << message.length() << std::endl;
return 0;
}Hello, C++! Length: 11
同じ型のデータを複数個まとめて扱いたい場合、配列を使います。C++にはいくつかの配列の形がありますが、ここでは代表的な3つを軽く紹介します。
C言語から引き継がれた、最も基本的な配列です。
#include <iostream>
int main() {
// int型の要素を5つ持つ配列を宣言し、初期化
int scores[5] = {88, 92, 75, 100, 69};
// 要素へのアクセス (インデックスは0から始まる)
scores[2] = 80; // 3番目の要素を80に変更
std::cout << "3番目のスコア: " << scores[2] << std::endl;
}3番目のスコア: 80
特徴:
Cスタイル配列を安全に使いやすくしたものです。サイズがコンパイル時に決まっている場合に使います。<array>ヘッダが必要です。
#include <iostream>
#include <array>
int main() {
std::array<int, 5> scores = {88, 92, 75, 100, 69};
// 安全なアクセス方法 .at()
scores.at(2) = 80;
std::cout << "3番目のスコア: " << scores.at(2) << std::endl;
// サイズを取得できる .size()
std::cout << "配列のサイズ: " << scores.size() << std::endl;
}3番目のスコア: 80 配列のサイズ: 5
特徴:
.size() で要素数を取得できる。.at(i) を使うと、範囲外のインデックスにアクセスしようとした際にエラーを検知してくれるため安全性が高い。プログラムの実行中にサイズを自由に変更できる、非常に柔軟で強力な配列です。迷ったらまず std::vector を検討するのが良いでしょう。<vector>ヘッダが必要です。
#include <iostream>
#include <vector>
int main() {
// 最初は3つの要素を持つ
std::vector<int> scores = {88, 92, 75};
// 末尾に新しい要素を追加 .push_back()
scores.push_back(100);
std::cout << "現在のサイズ: " << scores.size() << std::endl;
std::cout << "最後のスコア: " << scores.at(3) << std::endl;
}現在のサイズ: 4 最後のスコア: 100
特徴:
std::array と同様の便利な機能 (.size(), .at() など) を持つ。int, double, char, bool といった基本的なデータ型が存在する。const を使うことで、変数を不変にし、プログラムの安全性を高めることができる。auto を使うことで、コンパイラに型を推論させ、コードを簡潔に書くことができる。std::string、配列はstd::vector** や std::array クラスを使って安全かつ便利に扱う。あなたの名前(std::string)、年齢(int)、視力(double)をそれぞれ変数として宣言し、コンソールに出力するプログラムを書いてください。ただし、名前は一度決めたら変わらないものとして、constを使って宣言してください。
#include <iostream>
#include <string>
int main() {
}2つのstd::string変数 firstName と lastName を宣言し、あなたの姓名で初期化してください。その後、これら2つの変数を連結してフルネームをfullNameという新しい変数に格納し、そのフルネームと文字数(長さ)をコンソールに出力するプログラムを書いてください。
#include <iostream>
#include <string>
int main() {
}