C言語のコンパイラエラー C2064:原因と対処法について解説
この記事は、Visual Studio環境で発生するコンパイラエラー C2064について説明します。
C2064は、関数ではない変数やポインタを誤って関数呼び出しの形式で扱った場合に発生します。
特に、メンバー関数ポインタの呼び出しにおいて、オブジェクトのインスタンスコンテキストを正しく指定しないとエラーとなります。
具体例を交えて、正しい書き方を紹介しています。
エラー発生原因の解説
関数と変数の区別ミス
関数呼び出しと変数参照の誤認識
C言語では、関数と変数は明確に区別されており、変数を関数のように呼び出すとコンパイルエラーが発生します。
たとえば、実際の関数呼び出しと誤って変数への参照を行おうとすると、コンパイラはその変数が関数のポインタとして評価されないためエラーを出します。
以下のサンプルコードでは、整数変数であるi
と文字列を指すポインタ変数p
を関数として呼び出そうとした場合のエラー例を示しています。
#include <stdio.h>
int i = 5, j;
char* p = "Hello";
void sampleFunction() {
// 以下の呼び出しは、変数を関数のように利用しているためエラーとなります。
j = i(); // エラー: iは関数ではなく変数です。
p(); // エラー: pは関数ポインタではなく、単なる文字列ポインタです。
}
int main() {
sampleFunction();
return 0;
}
上記の例では、i
やp
に対して関数呼び出しの括弧()
を付けた記述により、実際の関数呼び出しであるかのように扱われようとするためコンパイラが正しく評価できずにエラーが発生します。
メンバー関数ポインタの誤用
インスタンスコンテキストの指定漏れ
クラスや構造体のメンバー関数ポインタを利用する場合、呼び出しには必ず対象となるオブジェクトのインスタンスが必要となります。
インスタンスが指定されない状態でメンバー関数ポインタを呼び出そうとすると、コンパイラエラーC2064が発生します。
これは、メンバー関数ポインタが単なるアドレスではなく、関数を呼び出すための実体となるオブジェクトのコンテキストも含むためです。
以下に、インスタンス指定が漏れている場合の例と正しい呼び出し方法の違いを示します。
#include <stdio.h>
struct C {
void func1() {
puts("func1 called");
}
void func2() {
puts("func2 called");
}
};
typedef void (C::*pFunc)();
int main() {
C c;
pFunc funcArray[2] = { &C::func1, &C::func2 };
// 以下の呼び出しはインスタンスコンテキストが指定されていないためエラーになります。
// (funcArray[0])();
// 正しくは、オブジェクト「c」のコンテキストから呼び出す必要があります。
(c.*funcArray[0])();
return 0;
}
この例では、funcArray[0]
というメンバー関数ポインタに対して、正しいインスタンスc
を指定せずに直接呼び出そうとするとエラーが発生します。
正しい呼び出し方法としては、(c.*funcArray[0])()
のように、対象となるインスタンスを明示的に指定する必要があります。
具体的なエラー事例
関数ではないものを関数として呼び出すケース
誤ったコード例の解説
以下のサンプルコードは、関数ではない変数を関数呼び出しの形式で使用してしまっている場合の例です。
整数変数i
や文字列ポインタp
には関数呼び出しの括弧が付けられており、その結果としてコンパイラエラーC2064が発生します。
#include <stdio.h>
int i, j;
char* p;
void errorExample() {
// 以下のコードは変数に対して関数呼び出しを行おうとしているため、コンパイルエラーとなります。
j = i(); // error: 'i' is not a function
p(); // error: 'p' does not point to a function
}
int main() {
errorExample();
return 0;
}
このコード例では、i
は整数変数であり、関数として呼び出すことはできません。
またp
は文字列ポインタであるため、括弧で呼び出す記述が誤りとなり、どちらもコンパイラからエラーが報告されます。
メンバー関数ポインタの不適切な呼び出し
誤用例とその影響
以下の例では、構造体C
のメンバー関数ポインタを利用して関数を呼び出す際に、誤ってインスタンスコンテキストを指定していないケースを示します。
この場合、呼び出し時にどのオブジェクトに対して関数を実行するかが不明となり、コンパイラエラーC2064が発生します。
#include <stdio.h>
struct C {
void func1() {
puts("func1 executed");
}
void func2() {
puts("func2 executed");
}
};
typedef void (C::*pFunc)();
int main() {
C c;
pFunc funcArray[2] = { &C::func1, &C::func2 };
// 以下のコードはインスタンスが指定されていない状態でメンバー関数ポインタを呼び出しているためエラーとなります。
// (funcArray[0])(); // 誤った呼び出し
// 正しい呼び出し方法では、対象となるオブジェクト「c」のコンテキストを指定します。
(c.*funcArray[0])();
return 0;
}
この例では、インスタンス指定を行わずにメンバー関数ポインタを呼び出そうとすることで、関数の実体が存在しないためにエラーが発生します。
正しい形は、インスタンスc
を通して関数が呼び出されるように記述する必要があります。
エラー対処法の解説
コード修正のポイント
正しい呼び出し構文の確認
エラーを解消するためには、関数呼び出し時に使用している変数やポインタが正しい型であるか、また適切なインスタンスが指定されているかを確認するポイントがあります。
・関数呼び出しの際は、関数ポインタまたは関数そのものを明示的に指定することを確認してください。
・メンバー関数の場合、必ず対応するオブジェクトのインスタンスを利用して関数を呼び出す必要があります。
呼び出し構文に誤りがないか、括弧()
の使用方法が正しいかを見直すことで、コンパイラエラーを解消する手助けとなります。
修正例の具体的手法
正しいインスタンス指定方法と呼び出し例
以下のサンプルコードは、メンバー関数ポインタの正しい呼び出し方法を示す例です。
この例では、クラスC
のコンストラクタでメンバー関数ポインタを適切に初期化し、メンバー関数を呼び出す際にthis
を用いてインスタンスが指定されています。
#include <stdio.h>
struct C {
// メンバー関数ポインタの型定義
typedef void (C::*pFunc)();
pFunc funcArray[2];
void func1() {
puts("func1 called");
}
void func2() {
puts("func2 called");
}
// コンストラクタでメンバー関数ポインタを初期化
C() {
funcArray[0] = &C::func1;
funcArray[1] = &C::func2;
}
void callFunction() {
// 以下の呼び出しはインスタンスコンテキストを指定しており、正しい呼び出しです。
(this->*funcArray[0])();
}
};
int main() {
C obj;
obj.callFunction();
return 0;
}
このコード例では、メンバー関数ポインタを使用してfunc1
を呼び出す際に、(this->*funcArray[0])()
という記述を用いることで、正しくインスタンスコンテキストが指定されています。
このように呼び出し構文を修正することで、コンパイルエラーC2064を回避することができます。
まとめ
この記事では、関数と変数の区別誤りや、メンバー関数ポインタ利用時のインスタンス指定漏れにより発生するコンパイラエラーC2064の原因と、その具体的な事例・対処法について説明しています。
正しい呼び出し構文の利用と、オブジェクトの明示的指定の重要性を実例を通じて理解できる内容です。