19.1 概要
省略
19.2 前処理指令の書き方
#include <iostream> // 最後の';'は不要
#define SIZE 1000 // 同上
#if JPNMODE
#include "jmode.h" // 先行空白があってもよい
#endif
# define DATA 2000 // '#'の後ろに空白があってもよい
行の終端に''があると次の行と併合されるという行継続機能があります。
#define charset(ch) (ch) == '!' || (ch)=='"' || (ch)=='#'
|| (ch)=='$' || (ch)=='%'
19.3 ファイルの挿入 #include
#include <ヘッダ名>
#include "ファイル名"
/* ファイルの指定方法 */
#includeは次のように使うこともできます。
何かの条件で、インクルードするファイルを切り替えるときなどに使うことができます。
#define INCFILE "myfile.h"
#include INCFILE
#includeを用いるときは、次のふたつのファイル形式を使い分けます。
name // 名前空間に対応しているヘッダ
name.h // 従来のヘッダ
/* C/C++標準ヘッダファイルの名前空間対応 */
C言語の時代から受け継がれたヘッダファイルも、標準C++使用に基づき、
新しい形式の名前空間対応版ヘッダが用意されました。
この場合は一貫した次のルールで新しい名前が付与されています。
<xxx.h>を<cxxx>にする
たとえば
#include <stdio.h> // 従来のヘッダ(.hが付く)
#include <cstdio> // 名前空間対応のヘッダ(.hが付かず、先頭に'c'を付与)
19.4 マクロ定義 #define
#define 識別子 文字列
#define 識別子(引数並び) 引数を含む文字列
/* オブジェクト形式マクロ */
#define MAXSIZE 256
n = MAXSIZE; ==> n = 256;
#define DIFF ('a' - 'A')
n = DIFF; ==> n = ('a' - 'A');
#define begin{
#define end }
if (a < 10) begin b = 20; end ==> if (a < 10) {b = 20;}
#define loop for(;;)
loop {...} ==> for(;;) {...}
#define then
if (b == 5) then c = 20; ==> if (b == 5) c = 20;
#define nothing
for (i = 0; i <= 9000; i++) nothing; ==> for (i = 0; i <=9000; i++) ;
/* 関数形式マクロ */
#define maxdata(a,b) ((a)>(b) ? (a) : (b))
n = maxdata(10,20); ==> n = ((10)>(20) ? (10) : (20));
#define rad(x) ((x)*3.14153/180.0)
n = tan(rad(30)); ==> n = tan(((30)*3.14159/180.0));
#define putd(n) cout << n << 'n'
putd(123); ==> cout << 123 << 'n';
#define getd(msg,n) cout << msg, cin >> n
getd("数値入力:", n); ==> cout << "数値入力:", cin >> n;
/* マクロの副作用 */
次の例は危険です。
#define mul(a,b) a*b
a = mul(x+100,y+200); ==> a = x+100*y+200;
これはあきらかにプログラマの意図と違っています。
だからこれはかっこをつけて付けて、
#define mul(a,b) (a)*(b)
a = mul(x+100,y+200); ==> a = (x+100)*(y+200);
これでも十分ではありません。
#define add(a,b) (a)+(b)
a = add(x,y)*300; ==> a = (x)+(y)*300;
これも意図した結果となりません。
したがってもうかっこを付けて、
#define add(a,b) ((a)+(b))
a = add(x,y)*300; ==> a = ((x)+(y))*300;
とするのが安全になります。
しかしこのように厳重にかっこを付けても防ぎきれないことがあります。
#define sqr(a) ((a)*(a))
a = sqr(++x); ==> a = ((++x)*(++x));
これでは++演算子が2回評価されてしまいます。
このように、マクロ処理は安全性が十分ではなく、
そこでC++ではマクロ定義の代わりにインライン関数を使うことが推奨されているわけです。
19.5 定義済みマクロ(組み込みマクロ)
19.6 マクロ定義の除去 #undef
#define SIZE 1000
となっていたとき、
#undef SIZE
とすると、このSIZEに関するマクロ定義は無かったことになります。
プリプロセッサには、マクロ定義があるかないかによって(定義内容には無関係に)処理を切り替える、
#ifdef SIZE // SIZEという定義があるなら
#ifndef SIZE // SIZEという定義がなければ
という用法がありますが、#undefはこれと対応して使うことができます。
このように#undefを使うと、定義名をそれが必要な範囲だけ有効にすることができます。
またマクロ定義はまったく同一の内容なら再記述してもえらーになりません。
#define SIZE 1000
#define SIZE 1000
しかし異なる内容で再定義すると警告メッセージが表示されます。
(警告なので実行は可能)。
このとき#undefを使うと警告なしに再定義することができます。
それは次のように行ないます。
#define SIZE 1000 // 最初の定義
...
#undef SIZE // 一度定義を取り消して
#define SIZE 2000 // 再定義する
関数形式マクロに対するundefも識別子だけを指定します。
#define maxdata(a,b) ((a)>(b) ? (a) : (b)) // 定義
#undef maxdata
19.7 条件付きコンパイル #if #else #elif #endif
#if 整数定数式
この部分をコンパイル
#elif 整数定数式
この部分をコンパイル
#elif 整数定数式
この部分をコンパイル
...
#else
この部分をコンパイル
#endif
はじめに真となった#ifまたは#elif部分のテキストをコンパイルします。
どの定数式も真とならなかったときは、#elseの部分をコンパイルします。
#else句も必要なければ省略できます。
#define MYMODE 640
#if MYMODE==640
char buf[32000];
#elif MYMODE>=512
char buf[16000];
#else
char buf[12000];
#endif
またよく使われる例としては、デバッグサポートがあります。
次のようにします。
#define DEBUG 1
...
#if DEBUG==1
cout << "debug: a=" << a << 'n';
#endif
条件式は論理演算子を使って、つなぐこともできます。
#if DEBUG==2 || DEBUG==3
19.8 定義ありの確認1 #ifdef #ifndef
#ifdef 識別子 // 識別子が定義されていたら
#ifndef 識別子 // 識別子が未定義なら
#define DEBUG // マクロ名の定義だけでよい。その値は不要
...
#ifdef DEBUG // DEBUGというマクロ名が定義されていたら
cout << "debug: a=" << a << 'n';
#endif
19.9 定義ありの確認2 #if defined #if !defined
#if defined 識別子 // 識別子が定義されていたら
#if defined(識別子) // 同上。かっこを付けてもよい
#if !defined 識別子 // 識別子が定義されていなかったら
#if !defined(識別子) // 同上。かっこをつけてもよい
#if defined 識別子 // 「#ifdef 識別子」と同じ
#if !defined 識別子 // 「#ifndef 識別子」と同じ
#ifdef DOSMODE // ifdefでは単純な条件しか書くことができない
#if defined(DOSMODE) && defined(SMALL) && SIZE==100 // 複合条件が可能
19.10 インクルードガード
// myhead.h
#ifndef MYHEAD_H
#define MYHEAD_H
...
#endif
このようにすると、「#include "myhead.h"」を複数回実行しても
定義部分が読み込まれるのは1回だけになります。
一度読み込まれると、MYHEAD_Hが定義されるので、
次回からは「#ifndef MYHEAD_H」で重複読み込みが回避されます。
19.11 行番号とファイル名の変更 #line
#line 行番号 // 定義済みマクロである__LINE__の内容を変更する
#line 行番号 "ファイル名" // 定義済みマクロである__LINE__と__FILE__の内容を変更する
19.12 コンパイラへのオプション指示 #pragma
#pragma 指示子
実際にこれらの#pragma指示子をどのように使うかは、
各処理系のマニュアルを読まなければなりません。
19.13 エラー表示 #error
#error エラーメッセージ
#define COND 2
#if COND == 1
int dt[100];
#elif COND == 2
int dt[250];
#elif COND == 3
int dt[500];
#else
#error 定数CONDは1-3の値しか設定できません。
#endif
のようにします。
ここでCONDの値を誤って、「#define COND 4」にすると、コンパイル時に
smp.cpp:13: : #error : 定数CONDは1-3の値しか設定できません
のようなエラーメッセージが出ます。
#errorで指定する文字列は""で囲む必要はありません。
19.14 前処理用演算子
/* #演算子 */
#は引数を文字列化します。
#define dataout(dt) cout << #dt "=" << dt << 'n'
dataout(nn); ==> cout << "nn" "=" << nn << 'n'
/* ##演算子 */
##はふたつの字句を結合します。
識別子を動的に作成するときなどに使うことができます。
#define strmake(a, b) a ## b
dt = strmake(base, 200); ==> dt = base100;
#define cat(a,b) a ## [b]
cout << cat(dt,i) << 'n'; ==> cout << dt[i] << 'n';
#define label(a,b) a ## b ## :
label(jmp, 20) ==> jmp20
19.15 ヌル指令 #
省略