お待たせしました。ようやくプリプロセッサーのお話です。
はい、#includeとかなにげに書いてきたものが何なのかわかります。
はじめに言うと、プリプロセッサのしごとをなるべくなくすべきです。なぜならばプリプロセッサには型の概念がないからです。
このことにより思わぬ副作用を招くことがあり、またその記法は特殊なものになってしまいます。
これだけ種類が有ります。解説するのは#includeと#defineにとどめます。他は
http://itref.fc2web.com/c/preprocessor.html
を参照してください。とてもわかり易いので。なお、#if/#else/#ifdef/#ifndefはこれまでもこれからもしれっと使います。
.cとか.cppが読み込めないわけではないのですが、ほぼ100%ヘッダーファイルを読み込むのに使います。
#include "DxLib.h" #include <stdio.h>
一般的なお話として、<>で囲むとコンパイラーの規定の場所とコンパイルオプションで指定した(gccなら-Iオプション)場所からヘッダーファイルを探します。
また、""で囲むと、それに加えて、#includeを書いたファイルと同じ場所も捜索対象になります。
ゆえに自分で作ったヘッダーは""で囲い、C言語標準ライブラリ―のヘッダーは<>で囲むのが普通です。ありきたり。
#defineはコンパイル前にソースコードを置換するものです。で、「マクロ」と呼ばれます。Excelとかのマクロとはちと違うので注意です。
#define WINDOW_HEIGH 1024
こんな風に定数っぽいのを作ることもできますし
#define MAX(A, B) (A > B)? A : B
という表現もできます。
ヘッダーファイルが複数読み込まれると、2重定義となりコンパイルエラーになります。この対策として
#ifndef FOO_H #define FOO_H //ヘッダーファイルの中身を書く #endif // FOO_H
といったことをします。こういう書き方をインクルードガードといいます。
インクルードガードで問題になるのが名前空間の衝突です。その対策として、特定の接頭辞をつける、UUIDをつけるなどがありますが、素直にファイル名の英字を全て大文字に直し、それ以外をアンダーバー ('_')にするというのもあります。その際には、この条件で同じマクロ名になってしまうファイルは置かない、"_H"で終わるマクロを他に定義しないといったことを決めておきます。特に同じマクロ名になってしまうファイル名は、普通にインクルードするときにも紛らわしいので使うべきではありません。
アンダーバー1文字と英字大文字1字、アンダーバー2文字のどちらかで始まるものは処理系に予約されているので、ライブラリでもない限り使うべきではありません。
#define _FOO_H // だめ #define __foo_h // これもだめ
C言語における定数は3つあります。
ではそれぞれ見て行きましょう。
constは例えばこんなふうに書けるのでした。
const int x = 7;
ところで、int
型のように小さい型ならいいのですが、この後出てくる構造体や大きな配列は大きいサイズなので、スタックにコピーされ、非効率です。
そこで
static const char foo[] = "foo";
のようにstatic
をつけるといいです。static
変数の生存期間はスコープに左右されず(だってスタックに積まれないもん)プログラムの開始から終了までとなります。
中にはconst
をつけるならstatic const
にするべきだ、という宗教も存在しますが、スタックにコピーされないものもあるのでおすすめしません。
一方で関数の外に書く場合は、グローバル変数(どこからでもアクセスできる変数)になるのを防ぐために必ずstatic
をつけるべきです。
こうすることで有効範囲がそのファイルに限定されます。includeしても使えません。
これは関数においても同様です。
これはプリプロセッサマクロで、例えばこんなふうに書けるのでした。
#define VAR 5
ところで#define
マクロにはいくつかの問題点があり、あまり使うことはありません。では問題点を見て行きましょう。
早速例を見ましょう。
#define BASE_SIZE 1 << 8 int main() { auto tmp = BASE_SIZE + 4; return 0; }
tmpの型はいうまでもなくint
ですね。で、tmpには何が入るでしょうか?260?いえいえ、4096です。なぜならばこれは以下のように置換されるからです。
auto tmp = 1 << 8 + 4;
つまり
auto tmp = 1 << 12;
となっています。まあ、この場合は
#define BASE_SIZE (1 << 8)
とすればいいんですけどね。
#defineの値はコンパイルより前、プリプロセス時に置換されます。なのでコンパイラーには変数名がわからないのです。これによりコンパイルエラーなどで原因究明が困難になります。
#defineとenum以外はすべてスコープの概念があります。
スコープの概念がないと何が問題かというと、#defineに関して言えば書いたファイルとそれをincludeしているファイルでその名前が消費されるので、変数名の衝突が起こしやすくなります。
参考サイト
定数の定義は,const intか,#defineか,それとも - わさっき
http://d.hatena.ne.jp/takehikom/20140807/1407420548
#defineの罠
http://www.geocities.co.jp/bleis_tift/cpp/baddefine.html
C言語のマクロの注意点
http://www.c-lang.org/detail/macro_caution.html