9

わかりました、技術的にはこれが未定義の動作であることは知っていますが、それにもかかわらず、私はこれを製品コードで何度も見てきました。間違っている場合は訂正してください。ただし、この「機能」を、現在の C++ 標準に欠けている側面、つまりアドレスを取得できないこと (まあ、メンバー関数の実際のオフセット)。たとえば、これは PCRE (Perl 互換の正規表現) ライブラリの一般的な実装から外れています。

#ifndef offsetof
#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field))
#endif

このような場合にそのような言語の微妙な点を利用することが有効かどうか、または必要であるかどうかについて議論することができますが、私はそれが次のように使用されていることも見てきました:

struct Result
{
   void stat()
   {
      if(this)
         // do something...
      else
         // do something else...
   }
};

// ...somewhere else in the code...

((Result*)0)->stat();

これはうまくいきます!の存在をテストすることでヌル ポインターの逆参照を回避し、ブロックthis内のクラス メンバーにアクセスしようとしません。elseこれらのガードが配置されている限り、それは正当なコードですよね? そのため、疑問が残ります。そのような構造を使用することでメリットが得られる実用的なユースケースはありますか? 最初のケースは言語制限の回避策であるため、2 番目のケースが特に心配です。またはそれは?

PS。C スタイルのキャストについては申し訳ありません。

4

7 に答える 7

9

最初のケースは何も呼び出さないことです。アドレスを取得しています。これは、定義され、許可された操作です。オブジェクトの先頭から指定されたフィールドまでのオフセットをバイト単位で生成します。このようなオフセットが非常に一般的に必要になるため、これは非常に一般的な方法です。結局のところ、すべてのオブジェクトをスタック上に作成できるわけではありません。

2番目のケースはかなりばかげています。賢明なことは、そのメソッドを静的と宣言することです。

于 2010-04-02T22:56:00.323 に答える
4

メリットは見当たりません((Result*)0)->stat();。これは醜いハックであり、遅かれ早かれ壊れる可能性があります。適切なC++アプローチは、静的メソッドを使用することResult::stat()です。

一方、offsetof()マクロは実際にメソッドを呼び出したり、メンバーにアクセスしたりすることはなく、アドレス計算のみを実行するため、offsetof()は有効です。

于 2010-04-02T23:05:31.483 に答える
4

他のすべての人は、動作が未定義であることを繰り返し繰り返しています。しかし、そうではなかったとしましょう。有効なポインターでなくてp->memberも、特定の状況下では一貫した方法で動作することが許可されています。p

2 番目の構造は、依然としてほとんど役に立たないでしょう。設計の観点からすると、メンバーにアクセスする場合としない場合の両方で 1 つの関数が機能する場合は、おそらく何か間違ったことをしている可能性があり、コードの静的部分を別の静的関数に分割する方がはるかに合理的です。ユーザーが操作する null ポインターを作成することを期待するよりも。

this安全性の観点から、無効なポインターが作成される可能性のある方法のごく一部のみを保護しました。手始めに、初期化されていないポインターがあります。

Result* p;
p->stat(); //Oops, 'this' is some random value

初期化されているが、まだ無効なポインターがあります。

Result* p = new Result;
delete p;
p->stat(); //'this' points to "safe" memory, but the data doesn't belong to you

また、常にポインターを初期化し、解放されたメモリを誤って再利用しない場合でも、次のようになります。

struct Struct {
    int i;
    Result r;
}
int main() {
    ((Struct*)0)->r.stat(); //'this' is likely sizeof(int), not 0
}

本当に、たとえそれが未定義の動作ではなかったとしても、それは価値のない動作です。

于 2010-04-03T07:33:50.097 に答える
3

特定のC++実装を対象とするライブラリがこれを行う場合がありますが、それは一般的に「正当」であるという意味ではありません。

これは問題なく動作します!これが存在するかどうかをテストすることでnullポインターの逆参照を回避し、elseブロックのクラスメンバーにアクセスしようとはしません。これらの警備員が配置されている限り、それは正当なコードですよね?

いいえ、一部のC ++実装では正常に機能する可能性がありますが、準拠するC++実装では機能しないことはまったく問題ありません。

于 2010-04-02T22:59:51.627 に答える
3

nullポインターの逆参照は未定義の動作であり、これを行うと何が起こる可能性があります。動作するプログラムが必要な場合は、それを行わないでください。

ある特定のテストケースですぐにクラッシュしないからといって、あらゆる種類のトラブルに巻き込まれないというわけではありません。

于 2010-04-02T23:00:20.393 に答える
2

未定義の動作は未定義の動作です。このトリックは、特定のコンパイラで「機能」しますか?まあ、おそらく。彼らはそれの次の反復のために働くでしょうか。または別のコンパイラ用ですか?おそらくそうではありません。あなたはあなたのお金を支払い、あなたはあなたの選択をします。私は、C ++プログラミングの25年近くの間、これらのことを行う必要性を感じたことは一度もないとしか言​​えません。

于 2010-04-02T22:59:27.683 に答える
1

声明について:

this の存在をテストすることでヌル ポインターの逆参照を回避し、else ブロック内のクラス メンバーにアクセスしようとしません。これらのガードが配置されている限り、それは正当なコードですよね?

コードは正当ではありません。ポインターが NULL の場合、コンパイラーやランタイムが実際にメソッドを呼び出すという保証はありません。NULL thisメソッドが実際にポインターで呼び出されるとは想定できないため、メソッドのチェックインは役に立ちません。

于 2010-04-02T23:07:56.980 に答える