Preprocessors はその名のとおり、コンパイルの前にソースコードを処理するプログラムです。 C / C++でプログラムを書いてから実行するまでに、いくつかのステップがあります。 実際にプリプロセッサについて学び始める前に、これらのステップを見てみましょう。
上の図に中間ステップを見ることができます。 プログラマーによって書かれたソースコードは、program.c というファイルに格納されます。このファイルは次にプリプロセッサによって処理され、program という名前の拡張ソース コード ファイルが生成されます。 この展開されたファイルはコンパイラによってコンパイルされ、program.objという名前のオブジェクトコード・ファイルが生成される。 最後にリンカがこのオブジェクト・コード・ファイルをライブラリ関数のオブジェクト・コードにリンクして、実行ファイルprogram.exeが生成されます。
プリプロセッサプログラムは、コンパイル前にソースコードを前処理するようコンパイラに指示するプリプロセッサディレクティブを提供します。 これらのプリプロセッサ指令はすべて ‘#’ (ハッシュ) 記号で始まる。 この記号は、#で始まる文がプリプロセッサ・プログラムに渡され、プリプロセッサ・プログラムがその文を実行することを示しています。 プリプロセッサ指示文の例としては、次のようなものがあります。 #include、#define、#ifndef などです。 シンボルはプリプロセッサーに渡すパスを指定するだけで、includeなどのコマンドはプリプロセッサーが処理することを覚えておいてください。 例えば、includeは、プログラムに余分なコードを含めます。 これらのプリプロセッサー指示文は、プログラムの任意の場所に配置することができます。
プリプロセッサ指示文には、主に4つの種類があります。
- Macros
- File Inclusion
- Conditional Compilation
- Other directives
ここで、これらの各ディレクティブについて詳しく学びましょう。
- マクロ。 マクロはプログラム中のコードの一部で、ある名前が付けられています。 コンパイラがこの名前に出会うたびに、コンパイラはその名前を実際のコード片に置き換えます。 マクロを定義するには、#define指令を使用する。 それでは、マクロの定義についてプログラムを使って説明しましょう。
#include <iostream>
#define LIMIT 5
int
main()
{
for
(
int
i = 0; i < LIMIT; i++) {
std::cout << i <<
"\n"
;
}
return
0;
}
#include <stdio.h>
#define LIMIT 5
int
main()
{
for
(
int
i = 0; i < LIMIT; i++) {
printf
(
"%d \n"
,i);
}
return
0;
}
出力されます。
01234
上記のプログラムでは、コンパイラがLIMITという単語を実行すると、それを5に置き換える。 マクロ定義の中の「LIMIT」という単語をマクロテンプレート、「5」をマクロ展開と呼ぶ。
注意:マクロ定義の最後にはセミコロン(‘;’)がない。 マクロ定義の最後にセミコロンは必要ありません。
引数を持つマクロ。 私たちは、マクロに引数を渡すこともできます。 引数で定義されたマクロは関数と同様に動作する。 これをプログラムを使って理解しよう。
#include <iostream>
#define AREA(l, b) (l * b)
int
main()
{
int
l1 = 10, l2 = 5, area;
area = AREA(l1, l2);
std::cout <<
"Area of rectangle is: "
<< area;
return
0;
}
#include <stdio.h>
#define AREA(l, b) (l * b)
int
main()
{
int
l1 = 10, l2 = 5, area;
area = AREA(l1, l2);
printf
(
"Area of rectangle is: %d"
, area);
return
0;
}
出力します。
Area of rectangle is: 50
上記のプログラムからわかることは、コンパイラがプログラム中にAREA(l, b)を見つけると、それを(l*b)という文に置き換えるということです。 これだけでなく、マクロテンプレートAREA(l, b)に渡された値も(l*b)というステートメントに置き換えられる。 したがって、AREA(10, 5) は 10*5 に等しくなる。
- ファイルインクルージョン。 このタイプのプリプロセッサ指令は、コンパイラにソースコード・プログラムにファイルをインクルードするように指示する。 ユーザーがプログラムに含めることのできるファイルには2つのタイプがある。
- ヘッダーファイルと標準ファイルである。 これらのファイルはprintf()、scanf()などのような事前に定義された関数の定義が含まれています。 これらのファイルは、これらの関数で動作するように含まれている必要があります。 異なる関数は、異なるヘッダーファイルで宣言されています。 例えば、標準的なI/O関数は’iostream’ファイルに、文字列操作を行う関数は’string’ファイルに含まれています。
構文
#include< file_name >
ここで、file_name はインクルードされるファイルの名前である。 <‘ と ‘>’ の括弧は、コンパイラが標準のディレクトリでファイルを探すように指示する。
- ユーザー定義ファイル。 プログラムが非常に大きくなった場合、小さなファイルに分割し、必要なときにインクルードするのが良い方法である。 このようなタイプのファイルはユーザー定義ファイルです。 これらのファイルは、次のようにインクルードすることができる。
#include"filename"
- Conditional Compilation: 条件付きコンパイルディレクティブは、いくつかの条件に基づいて、プログラムの特定の部分をコンパイルしたり、プログラムのいくつかの特定の部分のコンパイルをスキップするのに役立つディレクティブの一種である。 これは、2つのプリプロセスコマンド ‘ifdef’ と ‘endif’ の助けを借りて行うことができる。
構文
#ifdef macro_name statement1; statement2; statement3; . . . statementN;#endif
‘macroname’ という名前のマクロが定義されていれば、ステートメントのブロックは正常に実行されますが、定義されていなければ、コンパイラーは単にステートメントのこのブロックをスキップします。
- その他のディレクティブ。 上記のディレクティブの他に、あまり使われないディレクティブがもう2つある。 これらは
- #undefディレクティブ。 undefディレクティブは、既存のマクロの定義を解除するために使用される。 このディレクティブは次のように動作する。
#undef LIMIT
このステートメントを使用すると、既存のマクロ LIMIT の定義が解除されます。 このステートメントの後、すべての “#ifdef LIMIT” ステートメントは、false に評価されます。
- #pragma ディレクティブ。 このディレクティブは特別な目的のディレクティブであり、いくつかの機能をオンまたはオフにするために使用されます。 この種のディレクティブはコンパイラ固有のものであり、すなわちコンパイラによって異なるものである。 以下、#pragmaディレクティブのいくつかを説明します。
- #pragma startup と #pragma exit: これらのディレクティブは、プログラム起動前(main()に制御が移る前)とプログラム終了直前(main()から制御が戻る前)に実行する必要がある関数を指定するのに役立つ。
注意:以下のプログラムはGCCコンパイラーでは動作しません。
以下のプログラムを見てください。
- #pragma startup と #pragma exit: これらのディレクティブは、プログラム起動前(main()に制御が移る前)とプログラム終了直前(main()から制御が戻る前)に実行する必要がある関数を指定するのに役立つ。
C++#include <bits/stdc++.h>
using
namespace
std;
void
func1();
void
func2();
#pragma startup func1
#pragma exit func2
void
func1()
{
cout <<
"Inside func1()\n"
;
}
void
func2()
{
cout <<
"Inside func2()\n"
;
}
int
main()
{
void
func1();
void
func2();
cout <<
"Inside main()\n"
;
return
0;
}
C#include <stdio.h>
void
func1();
void
func2();
#pragma startup func1
#pragma exit func2
void
func1()
{
printf
(
"Inside func1()\n"
);
}
void
func2()
{
printf
(
"Inside func2()\n"
);
}
int
main()
{
void
func1();
void
func2();
printf
(
"Inside main()\n"
);
return
0;
}
出力します。
Inside func1()Inside main()Inside func2()
上記のコードは、GCCコンパイラで実行すると、以下のような出力を生成します。
Inside main()
これは、GCC が #pragma startup または exit をサポートしていないために起こります。 しかし、GCCコンパイラーで同じような出力をするために、以下のコードを使うことができます。
C#include <stdio.h>
void
func1();
void
func2();
void
__attribute__((constructor)) func1();
void
__attribute__((destructor)) func2();
void
func1()
{
printf
(
"Inside func1()\n"
);
}
void
func2()
{
printf
(
"Inside func2()\n"
);
}
int
main()
{
printf
(
"Inside main()\n"
);
return
0;
}
- #pragma warn 指令です。 このディレクティブは、コンパイル時に表示される警告メッセージを非表示にするために使用されます。
以下のように警告を非表示にすることができます。- #pragma warn -rvl: このディレクティブは、値を返すはずの関数が値を返さなかった場合に発生する警告を非表示にします。 このディレクティブは、関数が渡されたパラメータを使用しない場合に発生する警告を隠蔽します。 このディレクティブは,コードに到達できないときに発生する警告を隠蔽します. 例えば、関数の return 文の後に書かれたコードは到達不可能です。
この記事は Harsh Agarwal によって寄稿されました。 GeeksforGeeks が好きで貢献したい場合は、contribute.geeksforgeeks.org を使用して記事を書くか、[email protected] に記事を郵送することもできます。 あなたの記事が GeeksforGeeks のメインページに表示され、他の Geeks を助けることができます
。
- ヘッダーファイルと標準ファイルである。 これらのファイルはprintf()、scanf()などのような事前に定義された関数の定義が含まれています。 これらのファイルは、これらの関数で動作するように含まれている必要があります。 異なる関数は、異なるヘッダーファイルで宣言されています。 例えば、標準的なI/O関数は’iostream’ファイルに、文字列操作を行う関数は’string’ファイルに含まれています。