記事には広告を含む場合があります。

C言語入門講座

【C/C++】define(マクロ)による置き換え方法・関数との違いを解説

おゆ

組み込み系プログラマとして5年働いていた元エンジニアです。得意言語はC言語とC++。本サイトでは学生および新人組み込み系プログラマに向けてプログラミング知識をわかりやすく解説しています。

マクロには数字・文字列・式などを簡単な文字列に置き換えたり、関数の外に条件分岐を書けたりといった機能があります。

特に大きなプログラムを作る際にはよく使われます。

define(マクロ)とは何かわかりやすく解説

マクロとはプリプロセッサ命令の1つです。

プリプロセッサとはコンパイラより前に処理を行うプログラムのことです。

マクロはプリプロセッサ命令であることを活かして以下のような用途に使うことができます。

マクロの主な用途

  • 数字・文字・演算を簡単な文字に置き換える
  • 関数外での条件分岐

例えば円周率の値3.1415...をPIという簡単な文字列に置き換えられれば使い勝手がいいですよね。

このようにマクロを使えば数字などを簡単な文字に置き換えられます。

マクロで置き換えた値は後から書き換えられないので、書き換えられたくない値を置き換えるのにも重宝します。

ちなみに同じことグローバル変数でも可能です。グローバル変数は後から書き換え可能ですが、constを使えば書き換え防止にもできます。一方でコンパイラによってはマクロを使った方が若干プログラムを高速化できます。どちらを使ってもいいですが、可読性を考えてプログラム内ではどちらか一方に統一すべきです。

また、if文による条件分岐は関数内にしか書けませんが、マクロを使った条件文気は関数の外に書けるので違った使い方ができます。

おゆ
マクロは便利な反面、使いすぎるとコードが難解になってしまうので、必要最低限使うのがいいです。

define(マクロ)の使い方

数字・文字列の置換

マクロを使うと数字や文字列を指定したマクロ名に置換できます。

数字の置換方法

#define マクロ名 置き換えたい数字または文字列

文末にセミコロンは不要です。

マクロ名は他の変数と区別するために大文字で書くのが一般的です。

例1:

#include <stdio.h>
#define TITLE "What is the value of pi?"
#define PI 3.14f //整数の場合は型指定不要
void main()
{
    printf("%s\n",TITLE);
    printf("PI=%f\n",PI);
}

実行結果

What is the value of pi?
PI=3.140000

引数付き関数の定義

式の定義方法

#define マクロ名(引数) (式)

式を書く際、引数は必ず1つ1つカッコで囲います。

例2:

#include <stdio.h>
#define PLUS(x,y) ((x)+(y))
#define MINUS(x,y) ((x)-(y))
void main()
{
    printf("PLUS=%d\n",PLUS(2,1));
    printf("MINUS=%d\n",MINUS(2,1));
}

実行結果

PLUS=3
MINUS=1

何で引数を1つ1つカッコて囲うの?
おゆ
演算の優先順位をはっきりさせるためです。詳しくはこちらで解説しています。

文字列の定義・条件分岐①

文字列の定義方法

文字列の定義:
#define 文字列

マクロ名が定義されているという事実を示します。

定義した文字列を無効化:
#undef 文字列

条件分岐方法①

#ifdef マクロ名
    処理1
#elif
    処理2
#else
    処理3
#endif

指定したマクロ名が定義されていたら処理を実行します。

通常のif文のように必要に応じてelif(else if)やelseも使えます。

一行目は「#if defined(マクロ名)」と書いてもOKです。

例3:

#include <stdio.h>
#define PRODUCT1
#ifdef PRODUCT1
int function(int x, int y){return x+y;} //足し算
#else
int function(int x, int y){return x-y;} //引き算
#endif
void main()
{
    printf("function=%d\n",function(2,1));
}

実行結果

function=3

2行目でPRODUCT1を定義しているので、足し算の方のfunction関数が定義され、引き算の方のfunction関数は無視されます。

条件分岐②

条件分岐方法②

#ifndef マクロ名
    処理1
#elif
    処理2
#else
    処理3
#endif

指定したマクロ名が定義されていなかったら処理を実行します。

必要に応じてelifとelseも使えます。

例3の「#ifdef」を「#ifndef」に変更すると引き算の方のfunction関数が定義されます。

例4:

#include <stdio.h>
#define PRODUCT1
#ifndef PRODUCT1
int function(int x, int y){return x+y;} //足し算
#else
int function(int x, int y){return x-y;} //引き算
#endif
void main()
{
    printf("function=%d\n",function(2,1));
}

実行結果

function=1

define(マクロ)と関数の違い、メリットとデメリット

マクロを使うメリット

プログラムの高速化

プログラムを実行すると命令がメインメモリに保存され、CPUによって順番に実行されていきます。

プログラムが動く仕組み解説記事

【初心者向け】コンピュータとプログラムの仕組みを図解でわかりやすく解説

コンピュータってどんな仕組みで動いてるの?CPUとかメモリとかって聞くけど、どんな働きをしてるの?わかりやすく教えてほしい。 プログラムってパソコン上でどうやって動いてるの?初心者でも理解できるように ...

続きを見る

関数とマクロでは命令の保存され方が違います。

以下の図はメインメモリ内に保存されている命令のイメージです。
関数のイメージ

関数を呼び出すとその都度関数の内容が書いてある箇所までジャンプし、関数の命令を実行していきます。

終わったら元の場所に戻って次の命令を実行するという流れでプログラムが実行されます。

この場合、関数の命令が書いてある場所までジャンプし、戻るという処理がある分プログラムの実行速度は遅くなります。

マクロを使うと命令の中にマクロの命令が組み込まれるので、関数のように該当箇所にジャンプする必要がありません。
マクロのイメージ

ゆえにマクロの方がプログラムが高速化できる可能性があります。

関数外で条件分岐が可能

if文は関数内でしか使えませんが、マクロは関数外に記述できます。

そのためグローバル変数や関数などを定義するか否かを条件によって変えられます。

マクロを使うデメリット

予期しない動きをすることがある

マクロは書き方によって予期せぬ動きを動きをすることがあります。

例えば引き算をするMINUSというマクロとminusという関数を使ってみます。

例5:

#include <stdio.h>
#define MINUS(x,y) (x-y) //((x)-(y))と書くべき
int minus(int x, int y){return x-y;}
void main()
{
    printf("MINUS=%d\n",MINUS(2+3,1+2));
    printf("minus=%d\n",minus(2+3,1+2));
}

実行結果

MINUS=6
minus=2

どちらも実引数(呼び出し時に渡す引数)は同じで、(2+3)-(1+2)=2という計算結果を期待しています。

しかしマクロの実行結果は6になっています。

このときマクロは「2+3-1+2」という計算をしてしまっているのです。

これを防ぐには「#define MINUS((x)-(y))」と書くべきです。

このようにマクロを使うと思わぬミスが起きやすいです。

例5のように簡単な計算なら大丈夫ですが、複雑な演算を行うには関数の方が向いています。

プログラムが大きくなりやすい

メリットのところで解説したように、関数とマクロはメインメモリの使い方が違います。

関数は1か所に書いてある命令を使い回すので、メインメモリを節約できます。

一方でマクロを使うたびに命令の数は倍々で増えていくので、メインメモリの占領範囲が大きくなりがちです。

メインメモリはOSやブラウザなど別のプログラムも同時に使っているので、1つのプログラムが大量に占拠してしまうとコンピュータの動作が遅くなるなどの悪影響が出ます。

マクロと関数の使い分け

関数とマクロのメリットとデメリットを踏まえると、短くて簡単な演算はマクロ、それ以外は関数というように使い分けるのがいいでしょう。

ブログランキング参加中。クリックしてもらえると励みになります。

ブログランキング・にほんブログ村へ

-C言語入門講座