#pragma twice

KAB-studio > プログラミング > #pragma twice > 302 Version 15.02 関数とリンク

#pragma twice 302 Version 15.02 関数とリンク

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

 Version 15.02
関数とリンク

前回はソースファイルごとのコンパイルについて見てみました
一番前に表示してるソースファイルがコンパイルされて、その
ソースファイルの名前のオブジェクトファイルが作られるんだよね
そういうこと。で、その複数のオブジェクトファイルをリンクしてくっつ
けることで、 Exe ファイルが作られます
ってとこまではまだできてないんだけどね
そうだね。というわけで、まず Sub.cpp をこう修正して

#include <Windows.h>

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

なんか WinMain() とほとんど同じね
分かりやすくするために、ってことで。さて、この関数を WinMain() か
ら呼び出すってことを考えてみます
んー、今までの例で言うと、ヘッダーファイルに関数の宣言を書けばいい
んじゃない?  Version 2.12 ( No.023 ) みたいに
それが一番標準的な方法だね。ってことで、 Sub.h を作って
ほい。【ファイル】−【新規作成】で【ファイル】ページを開いて、
【C/C++ ヘッダーファイル】でファイル名は Sub.h 、っと
そこに、 Sub() の宣言を書きます

// Sub.h
void Sub();

そして、それを 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
    )
{
    Sub();
    return 0;
}

まず、 Sub.h をインクルードします

#include "Sub.h"

そしたらもう呼べるので、呼び出します

    Sub();

簡単ねー。ビルドして実行、お、 Sub() ダイアログ出た

で?
一見簡単に何気なく呼び出せてるように見えますが、この関数呼び出しに
は複雑な背景があるんです
そうなの? なんか全然普通に呼び出せてるけど
というわけで、ひとつずつ試してみましょう。 Debug フォルダの中を空
にして
ほい、ファイル削除っと
次に Sub.cpp の中の Sub() をコメントアウトしてください

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

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

……へ? コメントアウト?
そう。つまり関数そのものを削除ってこと
そんなことしちゃまずいんじゃ……
そのまずくなる所を細かく見ようっていう話。まず、この Sub.cpp を
コンパイルして
ほい、コンパイルっと。……通った……
そう、まずこの時点でコンパイルエラーにはなりません
なんか、コンパイルエラーになりそうだったんだけど……
では次、 Main.cpp をコンパイルして
ほい。 Main.cpp を手前に持ってきてコンパイルっと……え……ええええ
ええ!? 通っちゃった!! なんでコンパイルエラーにならないの!?
そう、コンパイルエラーにならないんです。じゃ、最後にリンクしてみ

う、うん。ビルドっと……あ、良かった、エラー出た……

--------------------構成: BuildTest - Win32 Debug--------------------
リンク中...
Main.obj : error LNK2001: 
    外部シンボル ""void __cdecl Sub(void)" (?Sub@@YAXXZ)" は未解決です
Debug/BuildTest.exe : fatal error LNK1120: 外部参照 1 が未解決です。
link.exe の実行エラー

BuildTest.exe - エラー 2、警告 0

でも、リンクの時にエラーが出るなんて……
なんでこうなるのか、をひとつずつ見ていきます。まず、話を分かりやすく
するために、話を Main.cpp に絞ります
 Sub.cpp は無視?
そう、無視。と、本当に無視してみようか
本当に無視?
ワークスペースの【FileView】の、【BuildTest】−【Source Files】を
開いてみて
うん。 Main.cpp と Sub.cpp が入ってる
この Sub.cpp を右クリックして
あ、メニューに【コンパイル】とかある!
それは置いといて、メニューの中から【設定】を選んで
ほい。【プロジェクトの設定】ダイアログが出たよ
右側の【一般】ページを開いて、【このファイルをビルドしない】を
チェックして
ほい。あ、アイコンが変わった……
下向き矢印が消えたでしょ。【OK】ボタン押してダイアログ閉じて、 
Main.cpp を次のように修正して

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

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

これでリビルドしてみて。通るでしょ
あ、ホントだ……
さっきの【このファイルをビルドしない】をチェックすると、その
ソースファイルはコンパイルされないんです
本当に無視するってこーゆーことなんだ。 Debug フォルダに Sub.obj も
ないし
さて、無視できたところで、 Main.cpp を今度はこう修正してみて

// 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() 関数の本体……がない、っていうのがつまりさっきのと同
じ状態ってこと?
そういうこと。関数の本体、つまり定義がなくて、宣言があるだけ
それでも……
そう。 Debug フォルダの中身を空にして、コンパイルしてみて
……通った
実は、関数を呼び出す時っていうのは、関数本体じゃなく、宣言を見てる
んです
ええっ!?
宣言がありさえすれば、コンパイルは通るんです
でも、リンクは通らない……
実はリンクでしているのは、関数の結び付けなんです。呼び出している所
と、関数本体をリンクの時に結び付けるんです
その時に本体がないからリンクでエラーになる!
それでこのエラー

Main.obj : error LNK2001: 
    外部シンボル ""void __cdecl Test(void)" (?Test@@YAXXZ)" 
    は未解決です

 Main.obj は関数を呼び出してるソースファイルのオブジェクトファイル
で、【外部シンボル】はその呼び出している関数のこと
そのあとの長いのが……
 "void __cdecl Test(void)" は?
あ、普通に関数だ。 __cdecl って Version 8.01 ( No.143 ) でやったの
だし
 (?Test@@YAXXZ) は、その関数の特殊な書き方
まー Test ってのは関数名なんだろうけど
で、この外部シンボル、つまり関数が見つからないので【未解決】ってこ

なるほど……
リンクするときに関数を探しているわけだけど、この探す相手が、
コンパイルしてできたオブジェクトファイル全部
え?
というわけで、まずさっきの Sub.cpp をコンパイルできる状態に戻して
さっきの【このファイルをビルドしない】のチェックを外すってこと?
そういうこと。そして Sub.cpp をこう修正して

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

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

って、ほとんどさっきと同じ……あ、呼び出す関数が Test() になって

これでリビルドすれば
【Sub()】ダイアログが出た!
というわけで次回に続く!

/*
    Preview Next Story!
*/
こうやってちゃんと理解していくことが大事?
そ。 LNK2001 のエラーが出ても、ちゃんと解決できるでしょ
今までだと、適当に色々試してうまくいけばいい、ってしてたかも……
次回はインクルードファイルについて正確な理解をしてもらいます
宣言を読み込むだけじゃないの?
実はソースファイル代わりにもなるんです
ええっ!?
というわけで次回
< Version 15.03 インクルードはただの置き換え >
につづく!
ほとんどヘッダーファイルだけでプログラムを組むこともできます
う、それはなんか嫌だ
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。