VC++の使い方

VC++の使い方 > リファレンス > ビルドエラー対処法
このページの内容
はじめに
C1004
C2360
C2440
C2660
C2664
C4007
C4312
C4786
LNK2001
ありがたいスポンサー様
All About ソフトウエアエンジニア
ネットで8%割引!自動車保険はアメリカンホーム・ダイレクト
人生の「チャンス」と「ピンチ」にモビット!
保険料が一生上がらない、保険料最大50%割引の一生涯の医療保険!

はじめに

初心者には分かりにくい VC++ のビルドエラー。一応、エラー番号を MSDN に入力すれば解説は出てくるが、どうにもこうにもわかりにくい。初心者にとっては 『じゃあ、実際にどうしたらいいのか』 という情報が抜け落ちているように思う。このリファレンスが、その疑問を解決できたら本望である。

ただし、ここに記述されている対処法がすべてではないし、ここに記述されていないエラーの方が多い。MSDN には、すべてのエラーについてのドキュメントがあるので詳細はそちらを参照してもらいたい。残念ながら、VC++ を使う以上は、MSDN に慣れなければならない。MSDN からほしい情報を自力で見つけ出せるようになったら、初心者ではないと宣言できる気がする。というか、MSDNはほんまに分かりにくいんだけどね・・・。

この一覧は、VC++ 6.0 でよく発生するエラーについて述べています。5.0以前や .NET でのエラーコードと互換性があるかどうかは未確認ですので注意してください。

C1004

メッセージ

予期せぬ EOF が検出されました。

意味

コンパイラはソースファイルをコンパイルする前に、プログラムの構成を処理するのだが、処理が終了する前にファイルの末尾に到達してしまった。これは、全ての左中カッコ({)に対応する右中カッコ(})がソースファイルに存在しないことを表している。

エラーの例

void main{
    if(true){
}

解決法

関数や構造体の定義の右中かっこ (}) が抜けている、クラス定義の右中かっこの後のセミコロンが抜けている、関数呼び出しや式のかっこが正しく整合しない、などの原因が考えられる。(MSDNより丸々コピペ^^;)

あるソースファイルでこのエラーが出たからといって、原因がそのソースファイル内にあるとは限らない。ソースファイルがインクルードしたファイルに EOF が検出されたときも、このエラーが出ることがあるので注意が必要。

C2360

メッセージ

'identifier' の初期化が 'case' ラベルによってスキップされました。

意味

switch-case の中では、変数を宣言を初期化できない場合がある。このようなときに、初期化できなかったということを表すエラー。

エラーの例

void main(){
	int x;
	switch ( x )
	{
		case 0 : 
			int i = 1; // エラー, case 1 によってスキップされます
			{ int j = 1; } // OK, 閉じたブロック内で初期化されています 
		case 1 : 
			int x = 1; // OK, 初期化はスキップされません
	}
}

解決法

case文の中に、中カッコ({})を記述すればよい。

switch ( x )
{
	case 0 :
	{
		// ...
	}
	case 1 :
	{
		// ...
	}
}

C2440

メッセージ

'conversion' : 'type1' から 'type2' に変換することはできません。

意味

'type1' から 'type2' にキャストできなかったことを表す。

エラーの例

void main(){
	char* pszBuf = malloc( 256 * sizeof(char)) ; 
	// 'void *' から 'char *' に変換することはできません。
}

解決法

switch-case の中では、変数を宣言を初期化できない場合がある。このようなときに、初期化できなかったということを表すエラー。 キャストが本当に妥当かどうかを確認する。明示的にキャストすればエラーが出なくなることはあるが、強引なキャストは判明しづらいバグをうむ可能性があるので注意すべきである。

上の例の場合は、

char* pszBuf = (char*)malloc( 256 * sizeof(char)) ; 

とすれば、エラーが出なくなる。

C2660

メッセージ

'function' : 関数が不正な number 個の実引数を伴って呼び出されました。

意味

関数 'function' が 'number' 個の引数で呼ばれたが、正しい引数の個数ではない。

エラーの例

void func( int, int );
void main()
{
   func( 1 );    //エラー, func( int ) が宣言されていません
   func( 1, 0 ); //OK, func( int, int ) が宣言されています
}

解決法

このエラーは、対策は簡単。関数呼び出しの引数の個数を正しく直せばOK。

ただし、メンバ関数とグローバル関数(Windows API やライブラリ関数など)が同じ名前の場合はこのエラーが出る可能性がある。メンバ関数をグローバル変数と別にすれば、このようなエラーは回避できるが MFC を使う場合は回避できないようだ。

この場合、メンバ関数を呼び出すときには this ポインタを、グローバル関数を呼び出すときにはスコープ演算子(::)を使って、それぞれを明示的に呼び出すとよい。

C2664

メッセージ

'function' : number 番目の引数を 'type1' から 'type2' に変換できません。

意味

'function' 関数の 'number' 番目の引数の型を、'type1' から 'type2' に変換できない。

エラーの例

void main(){
    const char  pszBuf[256] = "string" ;
    strcat( pszBuf, "add");
    //'strcat' : 1 番目の引数を 'const char [256]' から 'char *' に変換できません。
}

解決法

関数呼び出しの際、正しい引数を渡せていない。引数の順番を間違えた場合も、このエラーが出ることがある。

ライブラリ関数の場合は、ヘルプを参照して要求される型を確認するとよい。自分で定義した関数の場合、プロトタイプ宣言を確認して指定された引数を正しい型に修正しよう。明示的なキャストをすれば、このエラーが出なくなる場合もある。

この例の場合は、1番目の引数は char* であり、 const char* を渡すことはできないのでエラーが出た。

C4007

メッセージ

'identifier' : 'attribute' でなければなりません。

意味

'identifier'関数の属性は、'attribute' でなければならない、という警告。 エラーの例

#include <windows.h>
int WinMain( HINSTANCE, HINSTANCE, LPSTR, int)
{}
 // 'WinMain' : '__stdcall' でなければなりません。

解決法

WinMain関数は、 __stdcall として定義されなければならない。実際には WINAPI マクロが定義されているので、次のように定義・宣言するのが一般的である。

int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int)
{
   // 実装
}

C4312

メッセージ

'variable' : 'type' からより大きいサイズの 'type' へ変換します。

意味

'variable' という変数が 'type' から 'type' へキャストされることを警告している。64 ビットの整数を 32 ビットの値に代入するときに発生する警告。たとえば、32 ビット int または 32 ビット long を 64 ビット ポインタにキャストした場合に発生する。

エラーの例

SampbleStruct* p = (SampleStruct*)GetWindowLong(hDlg, GWL_USERDATA);

解決法

この警告は、VC++.NET で発生する。

32bit プログラミングでは、ポインタも LONG も 32bit(4Byte) であった。ところが、64bit プログラミングになると、ポインタは 64bit であり、LONG は32bit である。このため、LONG をポインタにキャストすると問題が発生する恐れがある。

この警告を表示しないようにするには、コンパイラオプションの /Wp64 (64 ビット移植への対応) を無効にするか

#pragma warning( disable : 4312 )   // C4312なんて表示すんな( ゚Д゚)ゴルァ

と記述するとよいだろう。

VC++.NET では、64bit プログラミングへの移行を助けるために、LONG をポインタに代入するときに警告を表示するようになった。とはいっても、VC++.NET 自体がデフォルトで 64bit プログラミングをサポートしているわけではないが、そろそろ 64bit への移行を考えながらプログラミングしてほしいという意向の現われだろう。

C4786

メッセージ

'identifier' : デバッグ情報で識別子が 'number' 文字に切り捨てられました。

意味

'identifier' という名前の識別子が長すぎたのでデバッグ情報が切り捨てられたことを表す警告。このエラーが出た箇所の変数は、デバッガでは表示、評価、変更、およびウォッチできない。実行する上では問題はない。

エラーの例

#include <map>
#include <string>
using namespace std ;

void main(){
    map< string, string> mymap ;
}

解決法

VC++ 6.0 のデバッガは、ある程度短い識別子しか認識できないことが原因。ただ、STL (特にコンテナ)を使用した場合は、自然と識別子が長くなり、この警告が頻発する。上のサンプルの場合、C4786 の警告が 92個も発生する。さすがに、うんざり・・・。

この警告を止めるには、ソースの先頭に

#pragma warning( disable : 4786 )   // C4786なんて表示すんな( ゚Д゚)ゴルァ

と記述すればよい。コンパイラが出してる警告を止めるなんて・・・と思うかもしれないが、MS の STL サンプルコードにもこの記述がある。ってことで、安心して止めてやりましょう。

LNK2001

メッセージ

外部シンボル "symbol" は未解決です

意味

中間ファイルの持つ外部参照を、リンカが結合する他のオブジェクトファイルとライブラリファイルから検索したが "symbol" が見つからなかった。

エラーの例

void my_func(); void main(){

   my_func();

}

解説

上のコードを XXXX.cpp としてビルドすると、

XXXX.obj : 外部シンボル ""void __cdecl my_func(void)" (?my_func@@YAXXZ)" は未解決です

というエラーが出る。XXXX.cpp 内では void my_func() が定義されていない。関数のプロトタイプだけしかを与えていない場合でもソースファイルはコンパイルできるので、XXXX.obj が生成され、 XXXX.obj に 「my_func関数を外部参照している」という情報が記述される。リンカはオブジェクトファイルをリンクする前に、他のオブジェクトファイルとライブラリファイルの中に、外部参照である my_func関数の定義があるか検索し、正しくリンクできるかどうかを確認する。検索した中に void my_func() の定義が存在しなかった場合、このエラーが起こる。

外部参照の例としては、関数やグローバル変数、ラベルなどがあり得る。

解決法

外部シンボルとして提示されたシンボルが自分で定義したシンボルか、ライブラリによって提供されるものかどうかで対応法が変わる。

自分で定義したシンボルの場合は、その関数の定義をしているかどうかを確認する。定義したはずなのに、このエラーが出る場合は、名前を間違えていないか、定義しているソースがちゃんとプロジェクトに入っているかどうか(コンパイルされるか)をチェックする。

ライブラリによって提供されるシンボルの場合は、ライブラリファイルとリンクしているかが重要なポイントとなる。例えば、 InitCommonControlsEx 関数は、Comctl32.lib の中に実体の情報が入っている。ただし、デフォルトの設定でビルドしても、Comctl32.lib はリンクされないため、このエラーが出る。

これを解決するには、プロジェクトにライブラリファイルを追加すればよい。もう一つの方法は、メニューの 『プロジェクト→設定』 からか Alt + F7 キーを押して プロジェクトの設定 ダイアログを表示し、プロジェクトを選択してリンクタブから 『オブジェクト/ライブラリ モジュール』 に 『Comctl32.lib』 を追加する。デフォルトでライブラリファイルは 『C:\Program Files\Microsoft Visual Studio\VC98\Lib』 にあるが、Platform SDK をインストールした場合は、そちらが最新のものである。

もう一つの原因として、ライブラリファイルのバージョンがヘッダファイルと異なる可能性もある。