ヌルポインターの逆参照に関する考察

そのようなコード&((T *)(0)-> x)が正しいかどうかの質問は非常に難しいことがわかります。 私はそれについて少しメモを書くことにしました。



PVS-Studioアナライザーを使用したLinuxカーネルのチェックに関する最近の記事で 、次のコードフラグメントを見つけたと書きました。

static int podhd_try_init(struct usb_interface *interface, struct usb_line6_podhd *podhd) { int err; struct usb_line6 *line6 = &podhd->line6; if ((interface == NULL) || (podhd == NULL)) return -ENODEV; .... }
      
      





また、私が書いた記事では、私の意見では、そのようなコードは間違っていると書いています。 詳細は記事に記載されています。



その後、私は間違っていたという手紙をメールで送られましたが、このコードは完全に正しいです。 podhd == 0の場合、このコードは基本的に「offsetof」イディオムを実装し、悪いことは何も起こらないことを多くの人が示しています。 たくさんの回答を書かないために、小さなブログ投稿の形で回答を作成することにしました。



当然、私はこのトピックをより詳細に研究することにしました。 しかし、正直なところ、結果として、私はさらに混乱しました。 したがって、あなたがそのように書くことができるかどうかにかかわらず、私はあなたに正確な答えを与えません。 いくつかのリンクのみを提供し、意見を共有します。



Linuxのチェックに関する記事を書いたとき、私はそのように考えました。



NULLポインターの逆参照は未定義の動作です。 未定義の動作の1つの兆候は、チェック(podhd == NULL)が消えたときのコードの最適化です。 これは、この記事で説明したシナリオです。



手紙の中で、一部の開発者は、コンパイラでこの動作を実現できないと書いています。 ただし、これは何も証明しません。 プログラムの予想される正しい動作は、未定義の動作のバリアントの1つにすぎません。



ffsetof()マクロが次のように機能することも書いています:

 #define offsetof(st, m) ((size_t)(&((st *)0)->m))
      
      





ただし、これは何も証明しません。 このようなマクロは、適切なコンパイラで正しく動作するように特別に作成されています。 同様のコードを記述する場合、動作する必要はまったくありません。



さらに、ここでコンパイラーは明らかに0を認識し、プログラマーが何を望んでいるかを推測できます。 0が変数に格納されている場合、これはまったく別の問題であり、コンパイラは予期しない方法で動作する可能性があります。



Wikipediaでのoffsetofの内容は次のとおりです。



マクロの「従来の」実装は、コンパイラがポインタに特にこだわりがあるわけではないことに依存していました。 アドレス0で始まる仮想構造を指定することにより、メンバーのオフセットを取得しました。



#define offsetof(st、m)((size_t)(&((st *)0)-> m))



これは、nullポインターを構造体stへのポインターにキャストし、その構造体内のメンバーmのアドレスを取得することで機能します。 これは多くのコンパイラで正常に機能しますが、C規格によると未定義の動作があります。これは、nullポインターの逆参照を伴うためです(ただし、式全体がコンパイル時に計算されるため、逆参照は行われないと主張するかもしれません)。 また、引数の1つにスペルミスがあると、複雑なコンパイラ診断が生成される傾向があります。 いくつかの最新のコンパイラ(GCCなど)は、代わりに特別な形式を使用してマクロを定義します。たとえば、



#define offsetof(st、m)__builtin_offsetof(st、m)



ご覧のとおり、ウィキペディアによると、私は正しいです。 あなたはそのように書くことはできません。 これは未定義の動作です。 StackOverflow Webサイトの一部も考えています: NULLポインターを介した構造体のメンバーのアドレス



しかし、誰もが漠然とした行動について話しているにも関わらず、この主題についての正確な説明はどこにもないことを気にします。 たとえば、ウィキペディアには、声明には確認が必要であるという注記があります[要出典]。



同様の問題はフォーラムで何度も議論されてきましたが、CまたはC ++標準への参照によってサポートされる明確な説明はどこにも見当たりません。



規格のこのような古い議論はまだありますが、これも明確さを追加しませんでした: 232.ヌルポインターによる未定義の動作は間接的ですか?



したがって、現時点では、最終的な質問は私には明らかではありません。 ただし、このコードはまだ不良であり、リファクタリングする必要があると思います。



誰かがこのトピックに関する良いメモを送ってくれたら、この記事の最後に追加します。



更新:続き: habrahabr.ru/company/pvs-studio/blog/250701



All Articles