メンバーポインタから構造体/クラス全体へのキャスト



次のコードを検討してください:


#include <iostream>
struct bar {
double a = 1.0;
int b = 2;
float c = 3.0;
};
void callbackFunction(int* i) {
auto myStruct = reinterpret_cast<bar*>(i) - offsetof(bar, b);
std::cout << myStruct->a << std::endl;
std::cout << myStruct->b << std::endl;
std::cout << myStruct->c << std::endl;
//do stuff
}
int main() {
bar foo;
callbackFunction(&foo.b);
return 0;
}

コールバック関数を定義する必要があり、その関数で追加情報を使用したいと考えています。独自の構造体を定義し、メンバーのアドレスを関数に渡しました。関数では、キャストによって構造体全体を「取得」したいのですが、ポインターが一致していないようで、間違った結果が得られます。キャスト中に何か間違ったことをしていると思いますが、何が原因かわかりませんか?


答え:


この作業を行うためのキャストがありません。オフセットを減算する前にバイト型にキャストしてから、 bar* に再キャストする必要があります .その理由は、マクロ offsetof オフセットをバイト数で返します。ポインター演算を行う場合、ポイントされた型のサイズに関して減算と加算が機能します。例を挙げてみましょう:


bar があると仮定しましょう b という名前のインスタンス それはアドレス 0x100h にあります。 sizeof(double) == 8 と仮定すると 、 sizeof(int) == 4 そして sizeof(float) == 4 、次に sizeof(bar) == 16 構造体とそのメンバーは、メモリ内では次のようになります:


b @ 0x100h
b.a @ 0x100h
b.b @ 0x108h
b.c @ 0x10Ch

offsetof(bar,b) 8 に等しい .元のコードは、「0x108h を bar 型の構造体を指しているかのように扱う」と言っています。 . bar を教えてください アドレス 0x108h - 8 * sizeof(bar) の構造体 、または具体的には:0x108h - 0x80h =88h.'元のコードが間違った計算を実行していた理由が、この例で示されていることを願っています。


これが、構造体の最初のメンバーの正しいアドレスを取得するために、代わりにアドレスをバイトとして減算することをコンパイラーに伝える必要がある理由です。


解決策は次のようになります:


bar* owner = reinterpret_cast<bar*>(reinterpret_cast<char *>(i) - offsetof(bar, b));


非常に注意する必要があることの 1 つは、 の場合にのみ合法です。 bar 標準レイアウトです .テンプレート std::is_standard_layout<bar>::value を使用できます 静的アサートを作成して、UB を誤って呼び出していないことを確認します。


いくつかのコードの回答


#include <iostream>
struct bar { double a = 1.0;
int b = 2;
float c = 3.0;
};
void callbackFunction(int* i) { auto myStruct = reinterpret_cast<bar*>(i) - offsetof(bar, b);
std::cout <<
myStruct->a <<
std::endl;
std::cout <<
myStruct->b <<
std::endl;
std::cout <<
myStruct->c <<
std::endl;
//do stuff } int main() { bar foo;
callbackFunction(&foo.b);
return 0;
}
b @ 0x100h b.a @ 0x100h b.b @ 0x108h b.c @ 0x10Ch 
char* tempPtr = reinterpret_cast<char*>(i) - offsetof(bar, b);
bar* myStructPtr = reinterpret_cast<bar*>(tempPtr);
struct bar {  // Must be standard-layout!   int b = 2;
// Must be first non-static data member! double a = 1.0;
float c = 3.0;
};
void callbackFunction(int* i) { auto myStruct = reinterpret_cast<bar*>(i);
std::cout <<
myStruct->a <<
std::endl;
std::cout <<
myStruct->b <<
std::endl;
std::cout <<
myStruct->c <<
std::endl;
//do stuff } int main() { bar foo;
callbackFunction(&foo.b);
return 0;
}