#pragma twice

KAB-studio > プログラミング > #pragma twice > 303 Version 15.03 インクルードはただの置き換え

#pragma twice 303 Version 15.03 インクルードはただの置き換え

前のページへ 表紙・目次へ 次のページへ

 Version 15.03
インクルードはただの置き換え

前回は、関数の呼び出しはリンクするときに解決するんだってことを説明
しました
あれにはビックリしたー。関数って、宣言さえあれば一応呼べちゃうの

コンパイルは通るからね。さて現在、ソースファイルとヘッダーファイル
は次のようになっていると思います

// Main.cpp
#include <Windows.h>

void Test();

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    Test();
    return 0;
}


// Sub.cpp
#include <Windows.h>

void Test()
{
    MessageBox( NULL, "サブ", "Sub()", MB_OK );
}


// Sub.h

void Sub();

そういえば、 Sub.h の Sub() の宣言ってほっといていいの?
宣言だけあって定義のない関数があっても、その関数を呼びさえしなけれ
ば大丈夫。関数を呼んじゃうと、リンクするときに定義がないからエラーに
なるけど
呼びさえしなければ問題ないんだ。あ、もうひとつ! Test() の宣言っ
て Sub.h になくていいの?
そう、そこがちょっと分かりにくい所かも。普通、関数の定義は
ソースファイルに、宣言はヘッダーファイルに書きます
だよね
でも、それは〈便利〉だからそうしてるだけで、必ずしもそうしなくてい
いんです
え”、そうなの?
というわけで、そもそもヘッダーファイルってなんなのか、って考えてみ
ます
ヘッダーファイルがなんなのか……?
まず、これを試してみて

// Main.cpp
#include <Windows.h>

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    MessageBox( NULL, "メイン", "WinMain()", MB_OK );
    return 0;
}

ん、フツーに実行できるよ。一番最初の例と変わらない……
では次。同じく  Main.cpp をこう修正して

// Main.cpp
#include <Windows.h>

#include "Sub.h"

えーっと…… WinMain() がないよ?
というわけで、 Sub.h に追加します

// Sub.h

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    MessageBox( NULL, "メイン", "WinMain()", MB_OK );
    return 0;
}

ええーっ!? ま、まさか……うわ、ビルドして実行、【メイン】
ダイアログ出たよ……どうして?
理由は簡単。 Main.cpp で Sub.h をインクルードしてるでしょ。実は、
インクルードは〈インクルードしたファイルと置き換わる〉ってことなんで

置き換わる……
つまり、実際にはこうなってるってこと

// Main.cpp
#include <Windows.h>

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    MessageBox( NULL, "メイン", "WinMain()", MB_OK );
    return 0;
}

うわ、同じ。つまりコピペされてるみたいなもんなのね……
そう、そこが重要。まず、今のインクルードの説明で、最初の Test() 
関数の宣言場所の話は分かったでしょ
そか、 Sub.h に書かれてても、実際にはインクルードした Main.cpp の
頭に書かれてるのと同じだから、関係ないんだ
そういうこと Main.cpp が

// Main.cpp
#include <Windows.h>

#include "Sub.h"

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    Test();
    return 0;
}

で、 Sub.h が

// Sub.h

void Test();

でも、インクルードしてるんだから

// Main.cpp
#include <Windows.h>

void Test();

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    Test();
    return 0;
}

と同じってことなんだ
だから、関数の宣言は別にヘッダーファイルでしなくてもいいってこと。
Test(); って呼び出す前に出てくればいいだけ
? 前に出てこなきゃダメ?
そう、だめ。たとえば

// Main.cpp
#include <Windows.h>

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    Test();
    return 0;
}

#include "Sub.h"

ってすると、コンパイルエラーになります

Main.cpp(11) : error C2065: 'Test' : 定義されていない識別子です。

そういえば、 #include みたいに、頭に # がついているものってなんて
言うか憶えてる?
プリプロセッサだよね、 Version 6.05 ( No.105 ) でやった。確か、
コンパイルするまえに……コンパイルする前?
そう、インクルードっていうのはコンパイル前にされるんです。だから、
イメージとしては、 Main.cpp は、コンパイル直前に、 Windows.h と 
Sub.h の中身がそのまま書き込まれていると思ってください
インクルードしたファイルがそのまま書き込まれる……そしてそれが
コンパイルされる!
そのコンパイルは、上から順に見ていきます。そして、関数宣言があれば
記憶して、関数呼び出しがあれば、憶えた関数宣言の中にあるかどうか調べ
て、あればリンク用に取っておいて、なければさっきの C2065 
上から見ていって、宣言があれば憶えておく……
しかも、これはコンパイル単位でするから……
だから?
次回に続く!

/*
    Preview Next Story!
*/
ヘッダーファイルって、ソースファイルにもなるのねー
でもそうすると不思議に思わない?
ヘッダーファイルなんていらない!
そういうこと。でもヘッダーファイルがあると便利なんです
なんで?
というわけで次回
< Version 15.04 ヘッダーファイルは必要? >
につづく!
残っているものにはちゃんと役割があるんです
深く考えずに使っちゃってるけどそういうもんなのね
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。