コンパイルとリンク方法

この資料では、Visual Studio の統合環境を利用してプログラムを作るのではなく、 Visual Studio Command Prompt からプログラムをビルドする方法を説明します

Visual Studio (C++) の統合環境 (プロジェクトファイル) を利用してビルドを行う場合は、 必ずしもこの内容は知らなくても良いかもしれません。

しかし、私は初心者の人こそ、最初はあまり自動化されていない方法で学んだほうが良いと思ってます。

背後で自動化されている部分をある程度理解しておいたほうが、今後、応用が利くからです。

好みの問題もあるのかもしれませんが、私は統合環境は、バージョンが上がるたびにいろんなことが変更され、 同じことをするにも覚えなおすことが増えるのが嫌ということもあり、ぶっちゃけほとんど使っていません(笑)

ただし、何か簡単なものを素早く作りたいときなど、たまに使う時もあります。
何が何でも使わないというのではなく、状況によって使い分けられれば良いと思います。

簡単なプログラムのビルド方法

ここでは Windows の基礎 「実行ユーザー名を取得する方法」 で紹介しているコードをビルドすることを通して、コンパイルとビルドの方法について説明します。

この資料の準備として、次のコードを test.cpp という名前で保存してください。

この資料では C:\temp ディレクトリに test.cpp を保存したことを想定して説明します。 C:\temp 以外のパスにファイルを保存した場合は、パスを適当に読み替えてくださいね。

#include <windows.h>
#include <lmcons.h>
#include <tchar.h>
#include <stdio.h>

int main(int argc, LPTSTR argv[]) {

     TCHAR szUserName[UNLEN + 1];
     DWORD dwUNLen = UNLEN;

     if( !GetUserName( (LPTSTR) szUserName, &dwUNLen ) ) {
          printf("Error: %u", GetLastError() );
          return 1;
     }

     _tprintf( TEXT("%s\n"), szUserName );

     return 0;
}

ちなみに、上記を例にとったのは単にコードが短いからです(笑)

同様の手順で他のコードもビルドできるようになるはずです。

ビルド、コンパイル、リンク... とは?

ビルドは、コンパイルとリンクを実行し、実行可能ファイルを作成する処理のことです。 コンパイルでは、ソースファイル (*.cpp や *.h) からオブジェクトファイル (*.obj) を生成します。 リンクでは、オブジェクトファイル (*.obj) やライブラリファイル (*.lib) から実行可能ファイルを作成します。

これはどういうことでしょうか。

言葉だけで説明するとわかりにくいと思ったので、図を描いてみました。


図1. コンパイルとリンク

後で細かく説明しますが、ざっと説明すると次の通りです。

まず図の左上にソースコードがあります。C/C++ ファイル (*.cpp) とヘッダファイル (*.h) です。

自前で書いた *.cpp ファイルと *.h ファイルから Windows API のヘッダファイルを 参照するなどして、コンパイルするとオブジェクトファイル (*.obj) が作成されます。

次に、オブジェクトファイル (*.obj) とライブラリ (*.lib) から、 リンカーによって実行可能ファイル (*.exe) を作成します。

尚、Windows SDK の環境では、C/C++ コンパイラ、リンカーはそれぞれ cl.exelink.exe です。 これらは Visual C++ Express Edition と同時にインストールした場合は Program Files\Microsoft Visual Studio 9.0\VC\bin にインストールされます。

コンパイル

Windows プログラムを正常にコンパイルするためには、Windows SDK 付属のヘッダファイルが必要です。 Visual C++ 2008 Express Edition をインストールすると以下の場所にインストールされます。

  • Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE
  • Program Files\Microsoft SDKs\Windows\v6.0A\include

cl はコンパイル時にデフォルトで INCLUDE 環境変数で指定されたディレクトリを確認します。

デフォルトで上記のパスが INCLUDE に設定されるというわけです。自分の環境で、環境変数を確認するためには set コマンドが利用できます。 以下のように findstr コマンドと共に使うと便利でしょう。


> set | findstr INCLUDE
INCLUDE=C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE;C:\Program 
Files\Microsoft SDKs\Windows\v6.0A\include;

それでは test.cpp をコンパイルしましょう。次のステップでコンパイル可能なはずです。

1. Visual Studio 2008 Command Prompt を開きます。

Visual Studio 2008 Command Prompt は Microsoft Visual C++ 2008 Edition → Visual Studio Tools の下にあります。

2. C:\temp に移動します。


> cd /d C:\temp

ここで、/d オプションはドライブが異なっても移動できるようにするためのオプションです。

3. コンパイルします。

特にオプションなしでコンパイルしてみましょう。


> cl /c test.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

test.cpp

この結果、C:\temp に test.obj というファイルが作成されればコンパイルは成功です。

ちなみに、ここで /c オプションを指定しているのは、コンパイルとリンクを同時に行わずに、コンパイルだけするようにするためです。

/c を指定しなければ、コンパイルとリンクを続けて行うことが可能です。しかし、コンパイラ、リンカーそれぞれへのオプションを明確にするためにも、 初心者の方はそれぞれのプロセスを別々に行うことをお奨めします。

ちなみに、ビルドプロセスを簡易化するためには、後で説明するように makefile と nmake コマンドが使えます。

リンク

ここでは上記のコンパイルの結果作成された test.obj から、実行可能ファイルである test.exe を作成します。

1. 必要なライブラリをリンクする

test.cpp では GetUserName API を使っているので、ライブラリファイル advapi32.lib を指定してリンクする必要があります。


> link advapi32.lib test.obj
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

この結果、実行可能ファイル test.exe が生成されます。

尚、どのライブラリを指定すればよいかわからない場合は、次のステップでそれを調べることが可能です。

まず、dumpbin コマンドを使って、インポートしている関数 (API) を調べます。


> dumpbin /SYMBOLS test.obj | findstr External
00C 00000000 SECT4  notype ()    External     | _main
00D 00000000 UNDEF  notype ()    External     | _printf
00E 00000000 UNDEF  notype       External     | __imp__GetLastError@0
00F 00000000 UNDEF  notype       External     | __imp__GetUserNameA@8
010 00000000 UNDEF  notype       External     | ___security_cookie
011 00000000 UNDEF  notype ()    External     | @__security_check_cookie@4

上記の出力の結果から、test.obj は main, printf, GetLastError, GetUserNameA を必要としていることがわかります。 名前の頭に付く、'_' や '__imp__' は無視してください。また名前のサイトに付く、'@<数字>' も無視してください。 パラメータの指定で、内部的に使用されているだけです。

また、GetLastNameA のように、関数名の最後に A または W が付いている場合はそれはそれぞれ次のように解釈します。

A は、Non-Unicode ビルド用の関数であることを示します。ANSI ビルドなどと言われます。 W は Unicode ビルド用の関数です。

Windows の API はほぼ全てマクロとして実装されており、同じ API を使っているつもりでもコンパイルの仕方によって (コンパイルオプションによって)、 ANSI ビルド用のコード、あるいは Unicode ビルド用のコードとしてコンパイルされます。

何も指定しないと、ANSI ビルドになります。

ANSI ビルドは、内部的にマルチバイトキャラクタと Unicode との変換が発生するため、効率が悪い上に文字化けの原因にもなります。 DLL や EXE ファイルのサイズが気にならないなら、常に Unicode ビルドを行うと良いでしょう。

補足: 別の機会に詳述しますが、Unicode ビルドを行う場合、UNICODE_UNICODE の二つの定数を指定します。

> cl /DUNICODE /D_UNICODE /c test.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

test.cpp

> dumpbin /SYMBOLS test.obj | findstr External
00C 00000000 SECT4  notype ()    External     | _main
00D 00000000 UNDEF  notype ()    External     | _wprintf
00E 00000000 UNDEF  notype ()    External     | _printf
00F 00000000 UNDEF  notype       External     | __imp__GetLastError@0
010 00000000 UNDEF  notype       External     | __imp__GetUserNameW@8
011 00000000 UNDEF  notype       External     | ___security_cookie
012 00000000 UNDEF  notype ()    External     | @__security_check_cookie@4

> link advapi32.lib test.obj
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

_main と _printf は C 言語の標準の関数です。また、上記からこのオブジェクトファイルは GetLastError と GetUserName API をインポートする必要があることがわかります。GetLastError と GetUserName を MSDN 等の資料で調べると (ググれば OK)、 それぞれ kernel32.lib 及び advapi32.lib を必要としていることがわかります。

ここで C ランタイムライブラリや kernel32.lib はデフォルトでリンクされますからそれらを明示的に指定する必要はありません。結局、 明示的に指定しなければならないライブラリは advapi32.lib だけです。

« 前の記事 次の記事 »
開発環境の構築方法 複数ファイルのコンパイルとリンク方法

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説