第15回 コンパイル前に処理を行う前処理プログラム
はじめに
前回は、プログラム実行中に必要なメモリを確保する動的メモリ確保について学びました。
これまでの講座では、
- 変数
- 条件分岐
- 繰り返し処理
- 配列
- 関数
- ポインタ
- 構造体
- ファイル操作
- メモリ管理
など、C++でプログラムを作成するための基本的な機能について学んできました。
しかし、C++のプログラムは、書いたコードがそのまますぐ実行されるわけではありません。
プログラムを実行するまでには、いくつかの処理が行われています。
その中の一つが前処理です。
前処理を理解することで、
#includeの意味#defineの役割- ヘッダファイルの仕組み
など、これまで何となく使用していた機能を正しく理解できるようになります。
今回の到達目標
- C++プログラムが実行されるまでの流れを理解する。
- 前処理とは何か説明できる。
#includeの役割を理解する。#defineを使用できる。- マクロの特徴を理解する。
C++プログラムが実行されるまで
C++のプログラムは、作成したソースコードをそのまま実行しているわけではありません。
大まかな流れは次のようになります。
ソースコード
↓
前処理
↓
コンパイル
↓
リンク
↓
実行ファイル作成
↓
プログラム実行
この中で、コンパイルを行う前に実行される処理を前処理と呼びます。
前処理とは
前処理とは、
コンパイルを行う前に、ソースコードを準備する処理
です。
前処理では、プログラムの内容を確認し、必要な部分を追加したり置き換えたりします。
前処理で使用される命令は、先頭に#を付けて記述します。
例えば、
#include <iostream>
や、
#define NUMBER 100
などがあります。
これらは通常のC++の命令とは異なり、プログラム実行時ではなく、コンパイル前に処理されます。
includeによるファイルの読み込み
これまで何度も使用してきた、
#include <iostream>
について詳しく見ていきます。
#includeは、
指定したファイルの内容を読み込む
ための前処理命令です。
例えば、
#include <iostream>
を書くことで、標準入出力機能を利用できるようになります。
そのため、
std::cout
std::cin
などを使用できます。
標準ライブラリとヘッダファイル
C++には、あらかじめ便利な機能が用意されています。
例えば、
ヘッダファイル
主な機能
iostream
標準入出力
string
文字列
fstream
ファイル操作
などがあります。
これらの機能を使用するには、対応するヘッダファイルを読み込む必要があります。
例えば、
#include <string>
を書くことで、文字列を扱うための機能を利用できます。
自作ヘッダファイル
#includeでは、自分で作成したファイルも読み込むことができます。
例えば、
main.cpp
calculator.h
という2つのファイルがある場合、
#include "calculator.h"
と書くことで、
calculator.h
の内容を利用できます。
<>と""の違いは、
書き方
対象
< >
標準ライブラリ
" "
自分で作成したファイル
という違いがあります。
defineによる置き換え
#defineは、文字を別の文字や値へ置き換えるために使用します。
例:
#define PI 3.14159
この場合、前処理によって、
PI
という部分は、
3.14159
へ置き換えられます。
使用例:
#include <iostream>
#define PI 3.14159
int main()
{
double radius = 5;
double area = PI * radius * radius;
std::cout << area;
}
マクロ
#defineを利用して作成する置き換え処理をマクロと呼びます。
例えば、
#define SQUARE(x) ((x)*(x))
というマクロを作成できます。
使用すると、
SQUARE(5)
は前処理によって、
((5)*(5))
へ置き換えられます。
結果として、
25
になります。
マクロを使うときの注意
便利なマクロですが、注意点があります。
例えば、
#define SQUARE(x) x*x
と書くと、
計算の優先順位によって予想外の結果になる場合があります。
そのため、
#define SQUARE(x) ((x)*(x))
のように括弧を付けます。
また、現在のC++では、単純な値の保存には#defineよりもconstを使用することが推奨されています。
constとの違い
例えば、
#define PI 3.14159
と、
const double PI = 3.14159;
は似ています。
しかし、違いがあります。
#define
const
処理される時期
前処理
コンパイル後
型
なし
あり
安全性
低い
高い
C++では、基本的にはconstを使用する方が安全です。
条件付きコンパイル
前処理では、条件によってコードを有効・無効にすることもできます。
例:
#ifdef DEBUG
std::cout << "デバッグ中";
#endif
この場合、
DEBUG
が定義されている場合だけ、内部のコードが有効になります。
主な用途は、
- デバッグ用コード
- OSごとの処理の変更
などです。
ヘッダファイルの重複読み込み
自作ヘッダを利用すると、同じファイルを複数回読み込んでしまう場合があります。
そのような問題を防ぐために、ヘッダファイルでは読み込み制御を行います。
代表的な方法として、
#ifndef SAMPLE_H
#define SAMPLE_H
// 内容
#endif
があります。
ただし、現在の段階では、
同じヘッダファイルを何度も読み込まないための仕組み
として理解しておけば十分です。
まとめ
今回は、コンパイル前に行われる前処理プログラムについて学びました。
- C++のコードは前処理を経てからコンパイルされます。
#includeはファイルの内容を読み込むために使用します。- ヘッダファイルを読み込むことで便利な機能を利用できます。
#defineは文字や値を置き換えるために使用します。#defineで作成する置き換え処理をマクロと呼びます。- 現在のC++では、単純な定数には
constを使用することが推奨されます。
前処理を理解することで、これまで使用していた#includeなどの意味を理解できるようになりました。
次回はいよいよ手続き型プログラミング編の最後として、安全なプログラムを作成するためのエラー処理について学びます。