2

のマクロoffsetof<cstddef>見ていて、可能な実装が経由であることがわかりました

#define my_offsetof(type, member) ((void*) &(((type*)nullptr)->member))

私はそれを試してみましたが、実際には期待どおりに動作します

#include <iostream>

#define my_offsetof(type, member) ((void*) &(((type*)nullptr)->member))

struct S
{
    char x;
    short y;
    int z;
};

int main()
{
    std::cout << my_offsetof(S, x) << '\n';
    std::cout << my_offsetof(S, y) << '\n';
    std::cout << my_offsetof(S, z) << '\n';

    S s;
    std::cout << (void*) &((&s)->x) << '\n'; // no more relative offsets
    std::cout << (void*) &((&s)->y) << '\n'; // no more relative offsets
    std::cout << (void*) &((&s)->z) << '\n'; // no more relative offsets
}

Live on Coliru

私が行った唯一の変更は、アドレスをポインターとして表示したいので、void*の代わりに最終キャストを使用することです。size_t

私の質問:

  1. コードは完全に合法ですか。つまり、 を介してメンバーに「アクセス」し、nullptrそのアドレスを取得することは合法ですか? そうであれば&(((type*)nullptr)->member)、メンバーのアドレスを 0 から相対的に計算しているように見えますが、これは本当ですか? (最後の 3 行で のアドレスに対するオフセットを取得しているようですs)。
  2. マクロ定義から最後のキャストを削除すると(void*)、segfault が発生します。なんで?&(((type*)nullptr)->member)type のポインターであってはなりませんかtype*、またはここで型が何らかの形で消去されていますか?
4

1 に答える 1

6
  1. コードは完全に合法ですか?

いいえ。未定義の動作です。コンパイラはoffsetofそのように実装することを選択するかもしれませんが、それはそれ実装であるためです。コンパイラは独自の機能を実装する方法を選択できます。一方、あなたはそのような「贅沢」を手に入れません。

offsetofマクロを実装する方法はありません。標準に準拠した方法ではありません。

  1. マクロ定義から (void*) への最終キャストを削除すると、segfault が発生します。なんで?&(((type*)nullptr)->member) は type* 型のポインターであるべきではありませんか、それともここで何らかの形で型が消去されていますか?

はおそらくC スタイルの string として出力しようとするmy_offsetof(S, x)ため、出力しようとしたことによるセグメンテーション違反です( xis でcharあり、その式の結果が になるchar*ため) 。std::ostreamoperator<<char*

于 2016-01-27T03:51:21.697 に答える