関数ポインタでサポートされているすべての操作は、生のポインタとは異なりますか? > 、 < 、 <= 、 >= 演算子は生のポインタでサポートされていますか? もしそうなら、その用途は何ですか?
5 に答える
関数ポインターとオブジェクト ポインターの両方について、それらはコンパイルされますが、それらの結果は、同じ完全なオブジェクトのサブオブジェクトへのアドレスに対してのみ一貫性があることが保証されます (クラスまたは配列の 2 つのメンバーのアドレスを比較できます)。またはそれ自体に反対します。
などを使用std::less<>
するstd::greater<>
と、任意のポインター型で機能し、それぞれの組み込み演算子の結果が指定されていない場合でも、一貫した結果が得られます。
void f() { }
void g() { }
int main() {
int a, b;
///// not guaranteed to pass
assert((&a < &b) == (&a < &b));
///// guaranteed to pass
std::less<int*> lss1;
assert(lss1(&a, &b) == lss1(&a, &b));
// note: we don't know whether lss1(&a, &b) is true or false.
// But it's either always true or always false.
////// guaranteed to pass
int c[2];
assert((&c[0] < &c[1]) == (&c[0] < &c[1]));
// in addition, the smaller index compares less:
assert(&c[0] < &c[1]);
///// not guaranteed to pass
assert((&f < &g) == (&f < &g));
///// guaranteed to pass
assert((&g < &g) == (&g < &g));
// in addition, a function compares not less against itself.
assert(!(&g < &g));
///// guaranteed to pass
std::less<void(*)()> lss2;
assert(lss2(&f, &g) == lss2(&f, &g));
// note: same, we don't know whether lss2(&f, &g) is true or false.
///// guaranteed to pass
struct test {
int a;
// no "access:" thing may be between these!
int b;
int c[1];
// likewise here
int d[1];
test() {
assert((&a < &b) == (&a < &b));
assert((&c[0] < &d[0]) == (&c[0] < &d[0]));
// in addition, the previous member compares less:
assert((&a < &b) && (&c[0] < &d[0]));
}
} t;
}
ただし、そのすべてをコンパイルする必要があります (ただし、コンパイラは必要なコード スニペットについて自由に警告できます)。
関数型には値がないため、pointee 型sizeof
に関して定義された操作は機能しません。これには次のものが含まれます。sizeof
void(*p)() = ...;
// all won't work, since `sizeof (void())` won't work.
// GCC has an extension that treats it as 1 byte, though.
p++; p--; p + n; p - n;
単項+
は任意のポインター型で機能し、その値を返すだけです。関数ポインターについては特別なことは何もありません。
+ p; // works. the result is the address stored in p.
最後に、関数ポインターへのポインターはもはや関数ポインターではないことに注意してください。
void (**pp)() = &p;
// all do work, because `sizeof (void(*)())` is defined.
pp++; pp--; pp + n; pp - n;
ポインターが同じ割り当てを指している場合は、ポインターを比較できます。たとえば、同じ配列の要素を指す 2 つのポインターがある場合、それらのポインターに対して不等比較演算子を使用できます。一方、異なるオブジェクトを指す 2 つのポインターがある場合、比較は「未定義」ですが、実際には、ほとんどのコンパイラーはおそらくアドレスを比較するだけです。
char *text[] = "hello";
const char *e_ptr = strchr(text, 'e');
const char *o_ptr = strchr(text, 'o');
if (e_ptr < o_ptr) { ... } // this is legal
char *other_text[] = "goodbye";
const char *b_ptr = strchr(other_text, 'b');
if (b_ptr > o_ptr) { ... } // not strictly legal
注※1 : 関数ポインタを呼び出すことができます。
注※2 : 関係演算子は、ポインター演算で使用してアドレスを相互に比較できるため、ポインターに対してサポートされています。実践例: 配列のトラバース
int data[5] = { 1, 2, 3, 4, 5 };
// Increment pointer until it reaches the end-address.
for (int* i = data; i < data + 5; ++i) {
std::cout << *i << endl;
}
演算子 <、>、<=、>= はポインターに対してサポートされていますが、比較される 2 つのポインターが同じメモリ割り当ての一部である場合にのみ、信頼できる結果を生成することが保証されます (配列割り当て内のインデックスへの 2 つのポインターを比較する場合など)。これらの場合、割り当て内の相対位置を示します (つまり、a < b の場合、a は配列内で b よりも低いインデックスを指しています)。同じ割り当てにないポインターの場合、結果は実装で定義されます (また、一部のアーキテクチャでは、マップに必要な互換性よりも厳密に違反する可能性があります。たとえば、64 ビット ポインターは、下位のみを使用して < または > を比較できます1 つの割り当てが 32 ビット ポインターに許可されているサイズを超えることができない場合は、32 ビット)。これらは、関数ポインターのコンテキストでは意味がありません。
その他の生のポインター操作: == は、ポインターが同じオブジェクトを指している場合に true を返します。- 2 つのポインター間のバイト数を生成します (同じ割り当てのみに適していると思いますか?)。+ は無意味なのでコンパイルしません。
関数ポインターの場合は、* で逆参照して呼び出すことができます。
メンバ関数へのポインタには、演算子 ->* と .* があります。
ポインターは、通常の整数値として表されます。他のすべての数値型でも許可されているポインターを使用してすべてを実行できます。+ - * / << >> == != ^ & | ! 〜%。何も忘れていないことを願っています。
関数ポインターは、() 演算子で呼び出すことができるという点でのみ異なります。