C言語のコンパイラエラー C2091 の原因と対策を解説
C言語で発生するコンパイラエラー C2091 は、関数を直接返す記述によって発生します。
C言語では関数自体を返すことはできず、代わりに関数へのポインターを返す必要があります。
この記事では、エラーの原因と修正方法について簡潔に説明しています。
関数返却に関する基本
関数が返せない理由
C言語では、関数そのものは値として扱えず、メモリ上にコピー可能なオブジェクトではありません。
そのため、関数を直接返すことはできません。
例えば、関数はコードとして実体が存在しており、変数のように値を保持することができないため、関数の戻り値として別の関数を指定するとエラーが発生します。
C言語においては、関数のアドレス(つまり関数ポインター)を返すことで、関数の呼び出しや処理の切り替えが行われます。
また、コンパイラは関数自体ではなく、関数のアドレスを返す形式がC言語の文法に適していると定義しているため、直接関数を返す記述は認められていません。
関数ポインターの役割
関数ポインターは、関数のアドレスを保持する変数の一種です。
これにより、関数を他の関数に渡す、または返すことが可能となります。
関数ポインターを利用することで、動的に関数を選択したり、コールバックとして利用することができます。
例えば、以下のコードでは関数ポインターを用いて、関数のアドレスを返し、そのアドレスをもとに関数を実行しています。
#include <stdio.h>
// 返却対象となる関数(サンプル関数)
int add(int a, int b) {
return a + b;
}
// 関数ポインターを返す関数
int (*getAddFunction())(int, int) {
return add; // 正しくは関数のアドレスを返す
}
int main(void) {
// getAddFunction()で関数ポインターを取得
int (*funcPtr)(int, int) = getAddFunction();
// 関数ポインターを使って関数を実行
int result = funcPtr(3, 4);
printf("Result: %d\n", result); // 出力例: Result: 7
return 0;
}
Result: 7
エラー C2091 の原因
誤った関数返却記述の例
関数をそのまま返そうとするとエラー C2091 が発生します。
たとえば、以下のサンプルコードは、関数を直接返そうとしており、コンパイラはこれを認めません。
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
// 誤った関数返却例:関数自体を返そうとしている
int getAddFunction() {
return add; // エラー C2091 が発生する記述
}
int main(void) {
// この部分でエラーになるため実行できません
int func = getAddFunction();
printf("Result: %d\n", func(3, 4));
return 0;
}
上記のコードでは、関数 getAddFunction
が int
型を返す指定になっていますが、実際には関数 add
のアドレス(すなわち関数ポインター)を返す必要があります。
シンタックスエラーの詳細
データ型不一致の具体例
関数を返す際、返却する型が関数自体の型と一致しないと、データ型不一致のエラーが発生します。
例えば、返却型が int
で宣言された関数に対して、戻り値に対して関数のアドレスを返そうとすると、型が一致せずエラーとなります。
正しくは下記のように関数ポインター型で定義する必要があります。
関数ポインター型は、関数の戻り値や引数の型を明確にして定義されなければならず、誤った宣言はコンパイラにエラーを与えます。
これにより、プログラムの型安全性を維持することができます。
コンパイラ警告の表示例
エラー C2091 の際、コンパイラは次のような警告メッセージを出力することがあります。
・「エラー C2091: ‘add’ :関数を直接返すことはできません」
・「戻り値の型と返却する型が一致しません」
このようなメッセージは、関数が直接返せないというC言語の仕様を反映しています。
エラー内容を確認することで、正しい関数ポインターの返却方法へ修正する手がかりとなります。
エラー C2091 の対策
正しい関数返却記述の方法
エラー C2091 を回避するためには、関数自体を返さず、その関数のアドレスを返す必要があります。
関数ポインターを用いることで、以下のように記述することができます。
#include <stdio.h>
// サンプル関数
int add(int a, int b) {
return a + b;
}
// 正しい関数返却例:関数ポインターを返す
int (*getAddFunction())(int, int) {
return add; // 関数のアドレスを返す
}
int main(void) {
// getAddFunctionで関数ポインターを取得
int (*funcPtr)(int, int) = getAddFunction();
int result = funcPtr(10, 20);
printf("Result: %d\n", result); // 出力例: Result: 30
return 0;
}
Result: 30
この方法では、関数 getAddFunction
の返却型を正しく int (*)(int, int)
と指定しており、関数 add
のアドレスを正しく返すことができます。
関数ポインター利用のポイント
関数ポインターを使用する際には、以下のポイントに注意する必要があります。
- 関数の戻り値や引数の型に合わせた正しいポインター型を定義する。
- 関数ポインターを利用することで、動的に処理を切り替える場合や、コールバック関数として利用することができる。
- 可読性を確保するため、関数ポインターを利用する際にはコメントや適切な命名を行う。
適切な宣言と使用により、プログラムの柔軟性が向上し、エラー C2091 のような問題を回避することができます。
修正例の紹介
修正前のコード例
以下は、誤った関数返却記述によってエラーが発生する例です。
#include <stdio.h>
int multiply(int x, int y) {
return x * y;
}
// 誤った記述:関数そのものを返そうとしている
int getMultiplyFunction() {
return multiply; // エラー C2091 が発生
}
int main(void) {
// エラーのため、このコードは正しく実行されません
int func = getMultiplyFunction();
printf("Result: %d\n", func(5, 6));
return 0;
}
修正後のコード例
次に、関数ポインターを用いて正しく関数のアドレスを返す例を示します。
#include <stdio.h>
int multiply(int x, int y) {
return x * y;
}
// 修正後:関数ポインターを返す記述に変更
int (*getMultiplyFunction())(int, int) {
return multiply; // 関数のアドレスを返す
}
int main(void) {
// 正しく関数ポインターを取得
int (*funcPtr)(int, int) = getMultiplyFunction();
int result = funcPtr(5, 6);
printf("Result: %d\n", result); // 出力例: Result: 30
return 0;
}
Result: 30
修正時の注意点
修正する際には、以下の点に注意してください。
- 戻り値の型宣言を関数ポインター型に変更する必要があること。
- 関数の引数や戻り値の型が一致しているか確認すること。
- デフォルトの関数宣言と関数ポインターの宣言は異なるため、混同しないようにすること。
正しい関数ポインターの宣言は、関数の引数リストおよび戻り値のデータ型を正確に表現するため、プログラマーは宣言ミスに注意することが大切です。
参考情報
他の関連エラーとの比較
関数返却に関するエラーは、エラー C2091 だけに留まりません。
例えば、
- エラー C2146:記号が不足している場合に発生することがあり、宣言の際の型や記号の不足などが原因です。
- エラー C4430:型指定子が不足している場合に発生するため、関数プロトタイプの宣言時などに間違いがあると出力されます。
これらのエラーも、宣言の正確性や記述のルールに関連しているため、コードの見直しが必要です。
関数返却 エラーの場合は、特に関数ポインターの宣言と使用方法に注目するとよいでしょう。
詳細なエラーメッセージの解説
エラー C2091 の場合、コンパイラからのメッセージは関数を直接返している箇所に対して発生します。
例えば、
「error C2091: ‘multiply’ :関数を直接返すことはできません」というメッセージは、返却の際に関数アドレスではなく関数自体を指定していることを示しています。
また、エラーメッセージ内の型情報を確認することで、正しい戻り値の型がどのように定義されるべきかのヒントを得ることができます。
具体的には、関数の戻り値として用いる場合、戻り値の型が関数ポインター型として定義されるべきであることを再確認するポイントとなります。
まとめ
この記事では、C言語では関数自体を返すことができず、必ず関数ポインターを返す必要がある点を解説しています。
エラー C2091 の原因として、不適切な戻り値の型指定や直接的な関数返却が挙げられること、またデータ型の不一致によりシンタックスエラーが発生する例を取り上げました。
さらに、正しい関数ポインターの宣言方法や利用時のポイント、修正例を具体的なサンプルコードとともに示しており、エラーの回避とコードの安全な実装方法が理解できます。