タグ

第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などの意味を理解できるようになりました。

次回はいよいよ手続き型プログラミング編の最後として、安全なプログラムを作成するためのエラー処理について学びます。