2009-02-21 29 views
331

मैं reinterpret_cast बनाम static_cast की प्रयोज्यता से थोड़ा उलझन में हूं। जो मैंने पढ़ा है, उससे सामान्य नियम स्थिर कास्ट का उपयोग करना है जब संकलन समय पर प्रकारों का व्याख्या किया जा सकता है इसलिए static शब्द। यह सी ++ कंपाइलर कास्ट है जो आंतरिक रूप से अंतर्निहित जानवरों के लिए भी उपयोग करता है।reinterpret_cast का उपयोग कब करें?

reinterpret_cast एस दो परिदृश्यों में लागू होते हैं, पूर्णांक प्रकारों को पॉइंटर प्रकारों में परिवर्तित करते हैं और इसके विपरीत या एक पॉइंटर प्रकार को दूसरे में परिवर्तित करते हैं। मुझे लगता है कि सामान्य विचार यह असंभव है और इससे बचा जाना चाहिए।

जहां मैं थोड़ा उलझन में हूं, मुझे एक उपयोग की आवश्यकता है, मैं सी से सी ++ को कॉल कर रहा हूं और सी कोड को सी ++ ऑब्जेक्ट पर पकड़ने की आवश्यकता है, इसलिए मूल रूप से इसमें void* है। void * और कक्षा के प्रकार के बीच कनवर्ट करने के लिए किस कास्ट का उपयोग किया जाना चाहिए?

मैंने static_cast और reinterpret_cast दोनों का उपयोग देखा है? हालांकि मैं जो पढ़ रहा हूं उससे यह प्रतीत होता है कि static बेहतर है क्योंकि कास्ट संकलन समय पर हो सकता है? हालांकि यह एक सूचक प्रकार से दूसरे में परिवर्तित करने के लिए reinterpret_cast का उपयोग करने के लिए कहता है?

+2

'reinterpret_cast' रन टाइम पर नहीं होता है। वे संकलन-समय दोनों बयान हैं। से http://en.cppreference.com/w/cpp/language/reinterpret_cast: "static_cast के विपरीत, लेकिन const_cast की तरह, reinterpret_cast अभिव्यक्ति किसी भी सीपीयू निर्देशों के संकलन नहीं है यह पूरी तरह एक संकलक निर्देश जो संकलक निर्देश देता है। अभिव्यक्ति के बिट्स (ऑब्जेक्ट प्रस्तुति) के अनुक्रम का इलाज करने के लिए जैसे कि यह प्रकार new_type था। " –

+0

@HeretoLearn, * .c और * .cpp फ़ाइल से प्रासंगिक कोड टुकड़े जोड़ना संभव है? मुझे लगता है कि यह सवाल के प्रदर्शन में सुधार कर सकते हैं। – OrenIshShalom

उत्तर

335

सी ++ मानक का पालन गारंटी देता है। यही कारण है कि निम्नलिखित में, एक, बी और सी में एक ही पते पर सभी बिंदु:

int* a = new int(); 
void* b = static_cast<void*>(a); 
int* c = static_cast<int*>(b); 

reinterpret_cast केवल गारंटी देता है कि आप एक अलग प्रकार, के लिए सूचक डाली और अगर तब reinterpret_cast इसे वापस मूल प्रकार के लिए, आपको मूल मूल्य मिलता है। निम्नलिखित में तो:

int* a = new int(); 
void* b = reinterpret_cast<void*>(a); 
int* c = reinterpret_cast<int*>(b); 

एक और एक ही मान c, लेकिन ख का मूल्य अनिर्दिष्ट है। (व्यावहारिक रूप से इसमें आम तौर पर एक और सी के समान पता होता है, लेकिन यह मानक में निर्दिष्ट नहीं है, और यह अधिक जटिल स्मृति प्रणालियों वाली मशीनों पर सत्य नहीं हो सकता है।)

शून्य से कास्टिंग के लिए *, static_cast को प्राथमिकता दी जानी चाहिए।

+16

मुझे यह तथ्य पसंद है कि 'बी' अपरिभाषित है। यह आपको इसके साथ मूर्ख चीजें करने से रोकता है। यदि आप किसी अन्य सूचक प्रकार में कुछ डालते हैं तो आप समस्याओं की मांग कर रहे हैं और तथ्य यह है कि आप इस पर निर्भर नहीं हो सकते हैं और आपको अधिक सावधान बनाते हैं। यदि आपने static_cast <> का उपयोग किया है तो ऊपर 'बी' क्या उपयोग है? –

+2

मैंने सोचा कि reinterpret_cast <> एक ही बिट पैटर्न की गारंटी है। (जो किसी अन्य प्रकार के वैध सूचक के समान नहीं है)। –

+1

@ मार्टिन - reinterpret_cast <> एक ही बिट पैटर्न में परिणाम की गारंटी नहीं है। "Reinterpret_cast <> द्वारा किए गए मैपिंग को कार्यान्वित किया गया है।" (सी ++ 03 5.3.10)। हालांकि, मानक नोट्स कि "यह अनिश्चित होने का इरादा है"। 'Reinterpret_cast' का उपयोग करते समय –

-14

FAQ पढ़ें! सी में होल्डिंग सी ++ डेटा जोखिम भरा हो सकता है।

सी ++ में, किसी ऑब्जेक्ट के पॉइंटर को किसी भी जानवर के बिना void * में परिवर्तित किया जा सकता है। लेकिन यह दूसरे तरीके से सच नहीं है। मूल सूचक वापस पाने के लिए आपको static_cast की आवश्यकता होगी।

16

reinterpret_cast का अर्थ सी ++ मानक द्वारा परिभाषित नहीं किया गया है। इसलिए, सिद्धांत में reinterpret_cast आपके प्रोग्राम को क्रैश कर सकता है। प्रैक्टिस कंपाइलर्स में आप जो भी उम्मीद करते हैं, उसे करने की कोशिश करते हैं, जो कि आप जिस प्रकार से गुज़र रहे हैं, उसके बिट्स की व्याख्या करना है जैसे कि वे उस प्रकार के थे जिन्हें आप कास्टिंग कर रहे थे। यदि आप जानते हैं कि आप किस कंपाइलर का उपयोग करने जा रहे हैं reinterpret_cast के साथ आप इसका उपयोग कर सकते हैं, लेकिन यह कहने के लिए कि यह पोर्टेबल झूठ बोल रहा है।

यदि आप वर्णन करते हैं, और बहुत अधिक मामले में जहां आप reinterpret_cast पर विचार कर सकते हैं, तो आप static_cast या इसके बजाय कुछ अन्य विकल्प का उपयोग कर सकते हैं। अन्य बातों के अलावा मानक यह है कि क्या आप static_cast (§5.2.9) की उम्मीद कर सकते हैं के बारे में क्या कहना है:

An rvalue of type “pointer to cv void” can be explicitly converted to a pointer to object type. A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value.

आपके उपयोग के मामले के लिए

इसलिए, यह काफी स्पष्ट है कि मानकीकरण समिति आप static_cast का उपयोग करने के लिए करना लगता है । पता बरकरार रखता है

static_cast करने और void* से एक सूचक ing:

+5

आपके कार्यक्रम को काफी दुर्घटनाग्रस्त नहीं है। मानक reinterpret_cast के बारे में कुछ गारंटी प्रदान करता है। जितनी बार लोग अपेक्षा करते हैं उतनी ही नहीं। – jalf

+0

ठीक है, reinterpret_cast स्वयं शायद दुर्घटनाग्रस्त नहीं होगा, लेकिन यह परिणामस्वरूप कुछ फर्जी परिणाम लौटा सकता है कि, जब आप इसका उपयोग करने का प्रयास करते हैं, तो दुर्घटना हो सकती है। – flodin

+1

यदि आप इसे सही तरीके से उपयोग नहीं करते हैं। यही है, ए से बी से ए तक reinterpret_cast पूरी तरह से सुरक्षित और अच्छी तरह से परिभाषित है। लेकिन बी का मूल्य अनिर्दिष्ट है, और हाँ, अगर आप उस पर भरोसा करते हैं, तो बुरी चीजें हो सकती हैं। लेकिन कास्ट स्वयं पर्याप्त सुरक्षित है, जब तक आप इसे मानक तरीके से इस्तेमाल करते हैं। ;) – jalf

115

एक मामला जब reinterpret_cast अपारदर्शी डेटा प्रकारों के साथ इंटरफेसिंग करते समय आवश्यक है। यह अक्सर विक्रेता एपीआई में होता है जिस पर प्रोग्रामर का कोई नियंत्रण नहीं होता है। यहाँ एक काल्पनिक उदाहरण है जहाँ एक विक्रेता के भंडारण और मनमाने ढंग से वैश्विक डेटा पुन: प्राप्त करने के लिए एक एपीआई प्रदान करता है:

// vendor.hpp 
typedef struct _Opaque * VendorGlobalUserData; 
void VendorSetUserData(VendorGlobalUserData p); 
VendorGlobalUserData VendorGetUserData(); 

इस API का उपयोग करने के लिए, प्रोग्रामर VendorGlobalUserData और फिर से वापस करने के लिए अपने डेटा कास्ट करना होगा। static_cast काम करेंगे नहीं, एक reinterpret_cast का उपयोग करना चाहिए:

// main.cpp 
#include "vendor.hpp" 
#include <iostream> 
using namespace std; 

struct MyUserData { 
    MyUserData() : m(42) {} 
    int m; 
}; 

int main() { 
    MyUserData u; 

     // store global data 
    VendorGlobalUserData d1; 
// d1 = &u;           // compile error 
// d1 = static_cast<VendorGlobalUserData>(&u);  // compile error 
    d1 = reinterpret_cast<VendorGlobalUserData>(&u); // ok 
    VendorSetUserData(d1); 

     // do other stuff... 

     // retrieve global data 
    VendorGlobalUserData d2 = VendorGetUserData(); 
    MyUserData * p = 0; 
// p = d2;           // compile error 
// p = static_cast<MyUserData *>(d2);    // compile error 
    p = reinterpret_cast<MyUserData *>(d2);   // ok 

    if (p) { cout << p->m << endl; } 
    return 0; 
} 

नीचे नमूना एपीआई के एक काल्पनिक दिया गया है:

// vendor.cpp 
static VendorGlobalUserData g = 0; 
void VendorSetUserData(VendorGlobalUserData p) { g = p; } 
VendorGlobalUserData VendorGetUserData() { return g; } 
+5

हां, यह reinterpret_cast का एकमात्र अर्थपूर्ण उपयोग है जिसके बारे में मैं सोच सकता हूं। – jalf

+7

यह एक देर से सवाल हो सकता है, लेकिन विक्रेता एपीआई इसके लिए 'शून्य *' का उपयोग क्यों नहीं करता है? – Xeo

+76

@Xeo क्योंकि विक्रेता एपीआई बेकार है। –

0
template <class outType, class inType> 
outType safe_cast(inType pointer) 
{ 
    void* temp = static_cast<void*>(pointer); 
    return static_cast<outType>(temp); 
} 

मैं समाप्त करने के लिए करने की कोशिश की और एक साधारण सुरक्षित डाली टेम्पलेट का उपयोग कर लिखा है । ध्यान दें कि यह समाधान किसी फ़ंक्शन पर पॉइंट पॉइंटर्स की गारंटी नहीं देता है।

+0

क्या? क्यों परेशान? यह ठीक है कि 'reinterpret_cast' पहले से ही इस स्थिति में करता है: "एक ऑब्जेक्ट पॉइंटर को एक अलग प्रकार के ऑब्जेक्ट पॉइंटर में स्पष्ट रूप से परिवर्तित किया जा सकता है। [72] जब ऑब्जेक्ट पॉइंटर प्रकार का _prvalue_' v' ऑब्जेक्ट पॉइंटर में परिवर्तित हो जाता है टाइप करें "पॉइंटर _cv_' टी' ", परिणाम 'static_cast है (static_cast (v))'। - N3797। –

+0

'C++ 2003' मानक के लिए मैं ** ** ** नहीं ढूंढ सकता हूं कि' reinterpret_cast' 'static_cast (static_cast (v))' –

+0

ठीक है, सच है, लेकिन मुझे 13 से संस्करण की परवाह नहीं है साल पहले, और न ही अधिकांश कोडर अगर (जैसा कि संभव है) वे इससे बच सकते हैं। उत्तर और टिप्पणियां वास्तव में नवीनतम उपलब्ध मानक को प्रतिबिंबित नहीं करनी चाहिए जब तक अन्यथा निर्दिष्ट नहीं किया गया ... IMHO। वैसे भी, मुझे लगता है कि समिति को 2003 के बाद इसे स्पष्ट रूप से जोड़ने की आवश्यकता महसूस हुई। (क्योंकि आईआईआरसी, यह सी ++ 11 में समान था) –

9

एक प्रयोग है अगर आप (आईईईई 754) को बिटवाइज़ संचालन लागू करना चाहते हैं तैरता है। इस का एक उदाहरण फास्ट व्युत्क्रम वर्ग-रूट चाल थी:

https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code

यह एक पूर्णांक के रूप में नाव की बाइनरी प्रतिनिधित्व व्यवहार करता है यह सही बदलाव और एक निरंतर से घटा देती है, जिससे आधा करने और प्रतिपादक negating ।

float Q_rsqrt(float number) 
{ 
    long i; 
    float x2, y; 
    const float threehalfs = 1.5F; 

    x2 = number * 0.5F; 
    y = number; 
    i = * (long *) &y;      // evil floating point bit level hacking 
    i = 0x5f3759df - (i >> 1);    // what the deuce? 
    y = * (float *) &i; 
    y = y * (threehalfs - (x2 * y * y)); // 1st iteration 
// y = y * (threehalfs - (x2 * y * y)); // 2nd iteration, this can be removed 

    return y; 
} 

यह मूल रूप से सी में लिखा गया था, इसलिए सी डाले का उपयोग करता है, लेकिन अनुरूप सी ++ डाली reinterpret_cast है: एक नाव को वापस परिवर्तित करने के बाद, यह इस सन्निकटन अधिक सटीक बनाने के लिए एक न्यूटन- Raphson यात्रा के अधीन है।

+1

'त्रुटि की जरूरत नहीं है: 'और डबल' टाइप करने के लिए एक rvalue 'int64_t {उर्फ लंबे पूर्णांक}' प्रकार की अभिव्यक्ति की अवैध डाली reinterpret_cast ((reinterpret_cast (घ) >> 1) + (1 एल << 61)) '- http://ideone.com/6S4ijc – Orwellophile

+0

क्षमा करें, मैं कोड को बहुत पुन: उत्पन्न करने की कोशिश कर रहा था (बहुत आलसी!) स्मृति। अब इसे सही मूल कोड से बदल दिया गया है। –

+0

मानक कहता है कि यह अपरिभाषित व्यवहार है: http://en.cppreference.com/w/cpp/language/reinterpret_cast ("टाइप अलियासिंग" के अंतर्गत) –

-3

त्वरित उत्तर: static_cast का उपयोग करें यदि यह संकलित होता है, अन्यथा reinterpret_cast का सहारा लें।

int x = 0x7fffffff://==nan in binary representation 

तो फिर तुम नाव की तरह एक अन्य प्रकार के रूप में एक ही चर का उपयोग करना चाहते हैं:

0

सबसे पहले आप यहाँ पूर्णांक की तरह एक विशेष प्रकार में कुछ डेटा है आप तय कर सकते

float y = reinterpret_cast<float&>(x); 

//this could only be used in cpp, looks like a function with template-parameters 
के बीच

या

float y = *(float*)&(x); 

//this could be used in c and cpp 

संक्षिप्त: इसका मतलब है कि एक ही स्मृति प्रयोग किया जाता है एक अलग प्रकार के रूप में। तो आप तैरने के लिए ऊपर की तरह int प्रकार के रूप में फ्लोट के बाइनरी प्रस्तुतियों को परिवर्तित कर सकते हैं। उदाहरण के लिए 0x80000000 -0 है (मंटिसा और एक्सपोनेंट शून्य हैं लेकिन साइन, एमएसबी, एक है। यह युगल और लंबे युगल के लिए भी काम करता है।

ऑप्टिमाइज़: मुझे लगता है कि reinterpret_cast को कई कंपाइलर्स में अनुकूलित किया जाएगा, जबकि सी-कास्टिंग पॉइंटररिथमेटिक द्वारा बनाई गई है (मान को स्मृति में कॉपी किया जाना चाहिए, कारण पॉइंटर्स सीपीयू-रजिस्ट्रार को इंगित नहीं कर सकते हैं)।

नोट: दोनों ही मामलों में आप कलाकारों से पहले एक चर में casted मूल्य को बचाने चाहिए! इस मैक्रो मदद कर सकता है:

#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; }) 
26

संक्षिप्त उत्तर: आप क्या reinterpret_cast के लिए खड़ा है पता नहीं है, उसका उपयोग नहीं करते। यदि आपको भविष्य में इसकी आवश्यकता होगी, तो आपको पता चलेगा।

पूर्ण जवाब:

की बुनियादी संख्या प्रकार पर विचार करें।

जब आप उदाहरण के लिए int(12) से unsigned float (12.0f) परिवर्तित करते हैं तो आपके प्रोसेसर को कुछ गणनाओं की आवश्यकता होती है क्योंकि दोनों संख्याओं में अलग-अलग प्रतिनिधित्व होते हैं। यह static_cast है।

दूसरी ओर, जब आप reinterpret_cast पर कॉल करते हैं तो CPU किसी भी गणना का आह्वान नहीं करता है। यह सिर्फ स्मृति में बिट्स का एक सेट मानता है जैसे कि यह एक और प्रकार था। तो जब आप इस कीवर्ड के साथ float* को int* कनवर्ट करते हैं, नया मान (सूचक dereferecing के बाद) गणितीय अर्थ में पुराने मूल्य के साथ कोई संबंध नहीं है।

उदाहरण: ऐसा नहीं है कि reinterpret_cast एक कारण की वजह से पोर्टेबल नहीं है सच है - बाइट क्रम (endianness)। लेकिन यह अक्सर आश्चर्यजनक रूप से इसका उपयोग करने का सबसे अच्छा कारण है। आइए उदाहरण की कल्पना करें: आपको फाइल से बाइनरी 32 बिट नंबर पढ़ना होगा, और आपको पता है कि यह बड़ा एंडियन है। आपका कोड सामान्य होना चाहिए और बड़े एंडियन (उदा। एआरएम) और छोटे एंडियन (उदा। X86) सिस्टम पर ठीक से काम करता है। तो आपको बाइट ऑर्डर की जांच करनी है। यह संकलन समय पर अच्छी तरह से जाना जाता है, ताकि आप constexpr समारोह लिख सकते हैं:

constexpr bool is_little_endian() { 
    unsigned short x=0x0001; 
    auto p = reinterpret_cast<unsigned char*>(&x); 
    return *p != 0; 
} 

स्पष्टीकरण: 0000'0000'0000'0001 स्मृति में x की बाइनरी प्रतिनिधित्व हो सकता है (बड़ा) या 0000'0001'0000'0000 (थोड़ा endian)। p सूचक के तहत बाइट को दोबारा डालने के बाद क्रमशः 0000'0000 या 0000'0001 हो सकता है। यदि आप स्थिर-कास्टिंग का उपयोग करते हैं, तो यह हमेशा 0000'0001 होगा, इससे कोई फर्क नहीं पड़ता कि अंतहीनता का उपयोग किया जा रहा है।

संबंधित मुद्दे