ポインターは、可変(非const)データまたは定数データへのポインターを指すものとして宣言できます。
関数を指すようにポインターを定義できます。
私の同僚と私はポインターでの "const"の使用について話し合っており、関数ポインターでのconst
の使用に関して疑問が浮上しました。
ここにいくつかの質問があります:
typedef void (*Function_Pointer)(void); // Pointer to void function returning void.
void function_a(Function_Pointer p_func); // Example 1.
void function_b(const Function_Pointer p_func); // Example 2.
void function_c(Function_Pointer const p_func); // Example 3.
void function_d(const Function_Pointer const p_func); // Example 4.
上記の宣言は、関数ポインターを組み込み型へのポインターのように扱う例です。
データ、変数、またはメモリポインタは、上記の組み合わせを可能にします。
質問は次のとおりです。関数ポインターは同じ組み合わせを持つことができ、const関数(例2など)へのポインターが何を意味するのでしょうか?
Cでは、関数がconst
であるなどのことはありません。そのため、const関数へのポインターは意味がありません(特定のコンパイラーでチェックしていませんが、コンパイルすべきではありません)。
これは異なりますが、関数へのconstポインター、constを返す関数へのポインターなどを使用できます。基本的に、関数自体以外はすべてconstにすることができます。いくつかの例を考えてみましょう:
// normal pointer to function
int (*func)(int);
// pointer to const function -- not allowed
int (const *func)(int);
// const pointer to function. Allowed, must be initialized.
int (*const func)(int) = some_func;
// Bonus: pointer to function returning pointer to const
void const *(*func)(int);
// triple bonus: const pointer to function returning pointer to const.
void const *(*const func)(int) = func.
関数へのポインターをパラメーターとして渡す限り、それは非常に簡単です。通常は、正しい型へのポインタを渡すだけです。ただし、任意のタイプの関数へのポインターを他のタイプの関数へのポインターに変換してから、元のタイプに戻し、元の値を保持することができます。
C仕様( C99 、セクション6.7.3)によると:
修飾型に関連付けられたプロパティは、左辺値である式でのみ意味があります。
仕様に「修飾された型」と記載されている場合、これはconst
、restrict
、orvolatile
キーワードで定義されたものを意味します。 Snice関数は左辺値ではないため、関数のconst
キーワードは意味がありません。あなたはある種のコンパイラ固有の拡張を見ているかもしれません。関数をconst
として宣言しようとすると、一部のコンパイラーはエラーをスローします。
あなたが定数関数へのポインターを見ていて、関数への定数ポインターではないことを確認していますか(つまり、pointerがconst
です) 、機能ではない)?
#4について:関数ポインタの作成、受け渡し、使用の概要については、 このガイド を参照してください。
Cでは、const
関数などはありません。 const
は型修飾子であるため、関数ではなく型の修飾にのみ使用できます。たぶん、あなたは関数へのconstポインタまたは関数への非constポインタを意味しますか?
C++では、メソッドはconst
にすることができます。メソッドがconst
の場合、そのメソッドを呼び出した後、そのメソッドを含むオブジェクトは、メソッドを呼び出す前と同じ状態になります(インスタンス変数[1]は変更されていません)。 。したがって、constメソッドとnon-constメソッドをポイントできますが、これらのメソッドは異なります。
引数リストの関数ポインタはretType (*variableName)(arguments)
として受け入れることができます。
[1] mutable
でない限り。
Cでは、関数GCCの世界にいる場合、関数canはconst
になります!関数は、関数や他のシンボルの宣言に付加された属性を使用してconst
として宣言できます。これは基本的に、その本体が使用できない場合でも関数が何を行うかについての情報をコンパイラーに提供するために使用されるため、コンパイラーは関数を使用して何らかの最適化を行うことができます。
定数関数は通常、pure
関数で定義されます。
純粋な関数は、基本的に副作用のない関数です。つまり、純粋な関数は、指定されたパラメーターとグローバルメモリに基づいて計算された値を返しますが、他のグローバル変数の値には影響しません。純粋な関数は、戻り値の型を合理的に欠くことはできません(つまり、戻り値の型が無効です)。
これで、const関数とは何かを定義できます。
グローバルメモリにアクセスせず、そのパラメータのみにアクセスする純粋な関数は、定数関数と呼ばれます。これは、関数がグローバルメモリの状態とは関係なく、同じパラメーターが指定された場合に常に同じ値を返すためです。したがって、戻り値は、指定されたパラメーターの値から直接かつ排他的に導出されます。
ここでconst
は、関数の可変性について何も意味しません。しかし、それはグローバルメモリに影響を与えない機能です。そのような関数に通常のポインタを割り当てることができます。とにかく、コード領域は一般的に(しばらくの間、自己変更コードを忘れている)ROとなり、通常のポインタを介して変更することはできません。
完全な 洞察に満ちた記事 をここで読んでください。
したがって、GCC定数関数に関しては、関数の可変性ではなく、最適化について話しています。
1.関数のcontents定数を作成するための「const」を構文的に配置する場所はありません。
Constがあるかどうかに関係なく、「function is not a l-value」エラーが発生します。
typedef void (* FUNC)(void);
FUNC pFunc;
pFunc = 0; // OK
*pFunc = 0; // error: Cannot assign to a function (a function is not an l-value)
typedef void (* const FUNC)(void);
FUNC pFunc;
pFunc = 0; // error (const)
*pFunc = 0; // error: Cannot assign to a function (a function is not an l-value)
typedef void (const * FUNC)(void); // error: <cv-qualifier> (lol?)
2&3.関数ポインタ-はい。
4.関数ポインタを安全に渡す方法はないと思います。世界のすべてのconstで、保護できる唯一のことは、 'SetCallback'がパラメータのローカルコピーを変更できないことです。
typedef void (* const FUNC)(void);
void SetCallback(const FUNC const pCallback)
{
FUNC pTemp = pCallback; // OK (even though pTemp is not const!!)
pCallback = 0; // error (const)
}
1.定数関数へのポインターと非定数関数へのポインターの意味は何ですか?
Constとnon-constの間に違いはありません。関数自体は変更できません。
注:C++では、関数がクラスのメンバー関数である場合、constは、この関数内のオブジェクトの状態を変更できないことを意味します(メンバー変数が割り当てられ、非constメンバー関数が呼び出されます)。この場合、constキーワードはメンバー関数のシグネチャの一部であるため、ポインターの点で違いがあります。
2.関数はconstですか?
上記を参照。
3.関数は非定数(可変)にすることができますか?
上記を参照
4.関数ポインターを渡すための適切な(安全な)構文は何ですか?
フリー関数へのすべてのポインターは、他のフリー関数へのポインターにキャストできます(つまり、それらのサイズは同じです)。したがって、(仮想)関数の型を定義し、void f();
を使用して、すべての関数ポインタをこの型に変換して保存できます。 注この共通の型を介して関数を呼び出さないでください:関数を元の関数へのポインタ型にキャストする必要があります。そうしないと、未定義の動作が発生します(おそらくクラッシュします)
C++の場合:メンバー関数へのポインターがフリー関数へのポインターに変換可能であるとは限りません