私はこれを何度も見て、特にさまざまなスレッド実装でC++を使用しました。これを行うことの落とし穴/問題があるかどうか私は疑問に思いますか?void *にキャストして再び戻るときに、エラーまたは未定義の状態に遭遇する可能性がある方法はありますか?そのような問題がある場合、どのように解決する必要がありますか?
ありがとうございました。
私はこれを何度も見て、特にさまざまなスレッド実装でC++を使用しました。これを行うことの落とし穴/問題があるかどうか私は疑問に思いますか?void *にキャストして再び戻るときに、エラーまたは未定義の状態に遭遇する可能性がある方法はありますか?そのような問題がある場合、どのように解決する必要がありますか?
ありがとうございました。
これは完全に有効です。これが標準がそれについて言わなければならないことです:
§4.10ポインタ変換
2オブジェクトタイプである「pointertocv」タイプの右辺値は、「 pointertocv」タイプの右辺値に変換できます。「 pointertocv」を「pointertocv 」に変換した結果は、オブジェクトがタイプ(つまり、基本クラスのサブオブジェクトではありません)。
T
T
void
T
void
T
T
つまり、クラスへのポインタをvoidポインタに変換できます。と ...
§5.2.9静的キャスト
10タイプ「pointertocv」の右辺値は、
void
オブジェクトタイプへのポインターに明示的に変換できます。「 pointertocv 」に変換され、元のポインタ型に戻るオブジェクトへvoid
のポインタ型の値は、元の値になります。
static_cast
これは、voidポインタを元のクラスポインタに戻すために使用できることを意味します。
それが役に立てば幸い。幸運を!
C ++では、次の場所に到達する ために静的キャストは必要ありませんvoid*
。
int main()
{
CFoo* foo = new CFoo;
void* dt = foo;
tfunc(dt); // or tfunc(foo);
return 0;
}
注意:キャストがtfunc()
必要なという点で、の実装は非常に正しいです。
私はC++でキャストするのを見たことがありません。void*
これは、C++で積極的に回避されているCの慣習です。
void*
すべてのタイプの安全性を取り除くために鋳造します。
reinterpret_cast
またはを使用static_cast
してポインタ型から同じポインタ型にキャストしたりvoid*
、同じポインタ型にキャストしたりする場合、実際には、結果が明確に定義されることが標準によって保証されています。
void*
正しいタイプが何であるかがわからなくなったため、間違ったタイプにキャストする可能性があるという危険があります。
これを行うことの落とし穴/問題があるかどうか私は疑問に思いますか?
バックを特定のタイプにキャストするときは、絶対に確認する必要がありvoid*
ます。そうしないと、未定義の動作と潜在的な災害が発生します。一度使用すると、型の安全性void *
が失われます。aが実際に指している型を追跡することは困難であり、型キャスト先の型を実際に指していることを保証または判断する方法はありません。void *
void *にキャストして再び戻るときに、エラーまたは未定義の状態に遭遇する可能性がある方法はありますか?
はい、で述べたシナリオ#1
。
そのような問題がある場合、どのように解決する必要がありますか?
void *
C ++で完全に使用することは避け、代わりにテンプレートと継承を使用してください。
Cでは、特定の状況で絶対に必要になる場合がありますが、使用を最小限に抑えるようにしてください。
結論として、
C / C ++を使用すると、足で自分を撃つことができます。そうするかどうかはあなた次第です。
標準で許可されているのは、が与えられた場合だけA* pa
です(A*)(void*)pA == pA
。結果
void* pv = pA;
A* pA2 = (A*)pv;
pA2->anything ...
と同じになりますpA->anything ...
他のすべては「定義されていません」、広告は-実際には-何らかの形で実装に依存しています。
私の経験に基づいて、ここにいくつかの既知の落とし穴があります:
A
派生形式B
、、pA
およびpB
であると考えA*
てくださいB*
。 pB=pA
のベースをpB
指すようにしA
ます。それはそれを意味するものpB
でpA
はなく、同じアドレスです。したがって、pB = (B*)(void*)pA
実際には他の場所をAに向けることができます(ただし、単一の継承オブジェクトは通常、同じオリジンを共有して実装されるため、正常に機能するようです)pB
同じことが逆です。実際にを指していると仮定するとA
、pA = (A*)(void*)pB
必ずしもAオブジェクトを正しく指しているとは限りません。正しい方法はpA = static_cast<A*>(pB);
class A: public Z, public B { ... };
場合、サブコンポーネントは同じAアドレスを持たないことを考慮してください。(そしてC ++の多重継承はiostreamがどこにでもあります)Z
A
B
(char*)(void*)pI
整数pI
を指す場合)は「ifin(-128 .. + 127)」と同じではありません*pI
(*pI
リトルエンディアンのマシンにのみ存在します)一般に、タイプ間の変換がアドレスの解釈方法を変更するだけで機能するとは限りません。
呼び出し元にデータを返すためにvoidポインターを使用するドライバーなどの多くの関数を知っていますが、スキーマはほとんど同じです。
int requestSomeData(int kindOfData, void * buffer, int bufferSize);
この関数は、パラメーターとしてさまざまなデータ型をとることができます。彼らがしていることは、バッファサイズをパラメータとして使用して、書き込みを行わないメモリの場所への書き込みを回避することです。bufferSizeが一致しないか、返されるデータよりも小さい場合、関数は代わりにエラーコードを返します。
とにかく:コードを書く前に、それらを使用しないか、3つ考えてください。
これは有効ですか?
はい、それは標準§5.2.9.7に従って有効です
タイプ「pointertocv1void」のprvalueは、タイプ「pointer to cv2 T」のprvalueに変換できます。ここで、Tはオブジェクトタイプであり、cv2はcv1と同じかそれ以上のcv-qualificationです。nullポインター値は、宛先タイプのnullポインター値に変換されます。「pointertocvvoid」に変換されたオブジェクトへのポインタ型の値は、おそらく異なるcv修飾で、元の値を持つ必要があります。[ 例:
T* p1 = new T;
const T* p2 = static_cast<const T*>(static_cast<void*>(p1));
bool b = p1 == p2; // b will have the value true.