C言語におけるマクロと関数の違いを徹底解説
はじめに
C言語では、マクロと関数はどちらも同じような処理を実現できますが、その内部動作には大きな違いがあります。マクロはプリプロセッサによって処理されるため、コンパイルの前にすべてのマクロが展開(置換)されます。一方、関数はプリプロセスされず、そのままコンパイルされます。
さらに重要な違いとして、マクロには型チェックが存在しません。そのため、異なる型の入力が渡された場合に問題が発生する可能性があります。また、入力値の扱い方が適切でないと、意図しない誤った結果を生成してしまうこともあります。次のプログラム例を見て、この問題を確認してみましょう。
サンプルコード
#include <stdio.h>
#define SQUARE(x) x * x
int sqr(int x) {
return x*x;
}
main() {
printf("Use of sqr(). The value of sqr(3+2): %d\n", sqr(3+2));
printf("Use of SQUARE(). The value of SQUARE(3+2): %d", SQUARE(3+2));
}
実行結果
Use of sqr(). The value of sqr(3+2): 25
Use of SQUARE(). The value of SQUARE(3+2): 11
なぜ結果が異なるのか
この関数とマクロは、本来どちらも同じ処理を行うはずですが、出力結果が異なっていることがわかります。その主な理由は以下の通りです。
関数の場合、引数として「3 + 2」を渡すと、まず式が評価されて「5」に変換されてから計算が行われるため、5 × 5 = 25 となります。
一方、マクロの場合は単純なテキスト置換が行われるだけなので、「3 + 2 * 3 + 2」という式に展開されます。C言語の演算子の優先順位により乗算が先に評価されるため、3 + 6 + 2 = 11 という予期しない結果になってしまうのです。
この問題を回避するには、マクロ定義時に引数を括弧で囲む(例:#define SQUARE(x) ((x) * (x)))方法がありますが、それでも後述するデメリットは残ります。
マクロが推奨されない理由
以上のような問題があるため、以下の理由からマクロの使用は推奨されません。
型チェックが行われない
デバッグが困難 ― 単純なテキスト置換のため、エラー発生箇所の特定が難しくなります。
名前空間(namespace)を持たない ― 一度定義されたマクロは、プログラム内のどこからでも利用可能になるため、意図しない衝突が起こる可能性があります。
コードサイズが増大する ― プリプロセス時にマクロがコードに展開されるため、使用箇所が多いほど実行ファイルが大きくなります。
コンパイル時のエラーチェックが行われない
まとめ
マクロは単純なテキスト置換であるため、型安全性やデバッグ性の面で多くの問題を抱えています。単純な定数定義など限定的な用途を除き、実際の処理には型チェックが行われる関数を使用することが、安全で保守性の高いC言語プログラミングの基本です。
-
C言語で正方形の中に正方形を表示するプログラムの作り方
プログラムの概要本プログラムは、C言語を使って「正方形の中に正方形」というパターンをコンソールに出力するものです。実行すると、以下のように二重の正方形が表示されます。アルゴリズム描画する外側の正方形の行数をユーザーから入力として受け取る。指定された行数をもとに、外側の正方形を表示する。外側の正方形の内側に、もう一つ小さな正方形を表示する。サンプルコード以下が、正方形の中に正方形を表示するC言語プログラムの完全なソースコードです。/* Program to print Square inside Square */#include <stdio.h>int main(){
-
正方形の中にある葉の形の面積を求める方法
ここでは、正方形ABCDの内部に存在する、下図のような葉の形(リーフ形状)の面積を求める方法を解説します。正方形の各辺の長さは「a」とします。この葉の形は、互いに等しい2つの部分で構成されています。各部分の面積を p とすると、次のようになります。したがって、葉全体の面積は 2p となります。計算式葉の形は半径 a の円弧によって囲まれた領域から成り立っているため、その面積は以下の公式で求められます。葉の面積 = a2 × (π/2 − 1)実装例(C++)#include <iostream> using namespace std; float leafArea(float a