2012-02-27 10 views
7

मुझे डर है कि मुझे कुछ मामूली याद आ रही है, लेकिन ऐसा लगता है कि यदि आप मूल हस्ताक्षरित मान को बनाए रखना चाहते हैं तो हस्ताक्षरित प्रकार से/में कनवर्ट करने का कोई वास्तविक सुरक्षित तरीका नहीं है।उसी आकार के हस्ताक्षरित/हस्ताक्षरित रूपांतरित करने के लिए कोई अनुपालन तरीका

reinterpret_cast पर, 5.2.10 पूर्णांक रूपांतरण के लिए एक पूर्णांक सूचीबद्ध नहीं करता है, इस प्रकार यह परिभाषित नहीं किया गया है (और static_cast कोई अतिरिक्त रूपांतरण परिभाषित नहीं करता है)। अभिन्न रूपांतरणों पर 4.7.3 मूल रूप से कहते हैं कि एक बड़े हस्ताक्षरित रूपांतरण को परिभाषित किया जाएगा (इस प्रकार पोर्टेबल नहीं)।

हम जानते हैं कि यह सीमित है, उदाहरण के लिए, uint64_t किसी हार्डवेयर पर, int64_t पर सुरक्षित रूप से परिवर्तनीय होना चाहिए और मूल्य में परिवर्तन किए बिना वापस। इसके अलावा मानक लेआउट प्रकारों के नियम वास्तव में सुरक्षित रूपांतरण की गारंटी देते हैं यदि हम memcpy को असाइन करने के बजाय दो प्रकार के बीच थे।

क्या मैं सही हूँ? क्या कोई वैध कारण है कि कोई अभिन्न प्रकार के आकार के बीच reinterpret_cast क्यों नहीं कर सकता है?


स्पष्टीकरण: निश्चित रूप से अहस्ताक्षरित के हस्ताक्षर किए संस्करण एक मूल्य गारंटी नहीं है, लेकिन यह केवल राउंड ट्रिप है कि मैं विचार कर रहा हूँ (अहस्ताक्षरित => हस्ताक्षर किए => अहस्ताक्षरित)


अद्यतन: उत्तर पर बारीकी से देखकर और मानक को पार करने के लिए, मुझे विश्वास है कि memcpy वास्तव में काम करने की गारंटी नहीं है, क्योंकि कहीं भी यह नहीं कहता है कि दो प्रकार लेआउट संगत हैं, और न ही चार प्रकार हैं। आगे अद्यतन, सी-मानक में खोदना इस memcpy काम करना चाहिए, क्योंकि लक्ष्य का आकार काफी बड़ा है और यह बाइट्स की प्रतिलिपि बनाता है।


उत्तर: वहाँ कोई तकनीकी कारण है कि reinterpret_cast इस रूपांतरण प्रदर्शन करने की अनुमति नहीं थी प्रतीत होता है। इन निश्चित आकार पूर्णांक प्रकारों के लिए memcpy काम करने की गारंटी है, और वास्तव में जब तक मध्यवर्ती सभी बिट-पैटर्न का प्रतिनिधित्व कर सकता है तो किसी भी मध्यवर्ती प्रकार का उपयोग किया जा सकता है (फ्लोट खतरनाक हो सकता है क्योंकि जाल पैटर्न हो सकते हैं)। आम तौर पर आप किसी भी मानक लेआउट प्रकार के बीच memcpy नहीं कर सकते हैं, वे संगत या चार प्रकार होना चाहिए। यहां इन्ट्स विशेष हैं क्योंकि उनके पास अतिरिक्त गारंटी है।

+0

मुझे लगता है कि आप हमेशा ऐसा कर सकते हैं, reinterpret_cast के साथ आप बस संकलक को बताएं कि स्थान मूल्य बदलने के बिना स्मृति स्थान की व्याख्या कैसे करें। –

+0

"किसी भी हार्डवेयर पर"। बिल्कुल यही बात है। शायद किसी भी हार्डवेयर पर जिसके साथ आप काम कर सकते हैं, लेकिन C++ को 2 अन्य पूरकों की तुलना में कुछ अन्य चीजों का समर्थन करने के लिए अकादमिक कारणों के लिए डिज़ाइन नहीं किया गया है, लेकिन वास्तव में ऐसे हार्डवेयर हैं। और (u) intXX_t प्रकारों को सिर्फ "as-if" computations में व्यवहार करने की आवश्यकता है, वे 2nds पूरक थे, हार्डवेयर की आवश्यकता नहीं है। – PlasmaHH

+0

@PlasmaHH, मेरा मुद्दा यह है कि 'uint64_t',' int64_t' सटीक आकार (यदि हार्डवेयर पर समर्थित है) हैं और इस प्रकार 'memcpy' को कनवर्ट करने की गारंटी है (मानक लेआउट प्रकारों के नियमों के माध्यम से)। मुझे यहां 2 के पूरक की परवाह नहीं है, मैं पीछे और आगे रूपांतरण चाहता हूं। –

उत्तर

2

आप बाहर बिंदु के रूप में, memcpy सुरक्षित है:

uint64_t a = 1ull<<63; 
int64_t b; 
memcpy(&b,&a,sizeof a); 

मूल्य ख अभी भी कार्यान्वयन परिभाषित किया गया है के बाद से सी ++ एक दो के पूरक प्रतिनिधित्व, की आवश्यकता नहीं है, लेकिन यह वापस परिवर्तित आप मूल मूल्य दे देंगे ।

बो पर्सन बताते हैं कि int64_t दो पूरक होगा। इसलिए memcpy के परिणामस्वरूप एक हस्ताक्षरित मान होना चाहिए जिसके लिए हस्ताक्षरित प्रकार पर सरल अभिन्न रूपांतरण वापस मूल हस्ताक्षरित मान के रूप में परिभाषित किया गया है।

uint64_t c = b; 
assert(a == c); 

इसके अलावा, आप अपने खुद के 'signed_cast' रूपांतरण के लिए आसान बनाने के लिए लागू कर सकते हैं (मैं दो के पूरक बात का लाभ लेने नहीं है क्योंकि इन intN_t प्रकार तक सीमित नहीं हैं):

template<typename T> 
typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value,T>::type 
signed_cast(typename std::make_unsigned<T>::type v) { 
    T s; 
    std::memcpy(&s,&v,sizeof v); 
    return s; 
} 

template<typename T> 
typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value,T>::type 
signed_cast(typename std::make_signed<T>::type v) { 
    T s; 
    std::memcpy(&s,&v,sizeof v); 
    return s; 
} 
+0

कोई अंतर्दृष्टि क्यों 'reinterpret_cast' इस रूपांतरण की अनुमति नहीं देगी? –

+1

यदि 'int64_t' परिभाषित किया गया है, तो यह दो पैडिंग बिट्स के साथ दो पूरक पूरक 64 बिट पूर्णांक होना चाहिए। तो यदि 'int64_t' परिभाषित किया गया है, तो 'memcpy' को काम करना चाहिए, और मैंने कभी ऐसे मामले के बारे में नहीं सुना है जहां एक साधारण कलाकार काम नहीं करेगा (हालांकि यह औपचारिक रूप से कार्यान्वित किया गया है)। मानक अभिन्न प्रकारों के लिए, जैसे 'int' या' long', यह कम यकीन है, चूंकि बिट्स और हस्ताक्षरित प्रतिनिधित्व की संख्या कार्यान्वित की गई है (और यदि कार्यान्वयन दो पूरक नहीं है, तो अजीब चीजें हो सकती हैं)। –

+0

@JamesKanze क्या आप निश्चित हैं कि int64_t' दो पूरक होना चाहिए? –

0

मुद्दा मूल रूप से है कि एक एन-बिट हस्ताक्षरित एन-बिट हस्ताक्षरित प्रकार में एक प्रतिनिधित्व है। उदाहरण के लिए, 8-बिट हस्ताक्षरित अधिकतम 256 का मान है, जबकि आवश्यक रूप से 8-बिट हस्ताक्षरित मान 128 से अधिक मूल्य नहीं हो सकता है (और ध्यान दें कि यह हार्डवेयर कार्यान्वयन के बावजूद है: कोई भी प्रतिनिधित्व के लिए थोड़ा सा आवश्यकता होगी साइन इन करें।)

+0

आह पर squawk होगा, लेकिन यह बस है, मानक लेआउट प्रकार _guarantee_ के नियम जो आप मूल्य में किसी भी हानि के बिना आगे और आगे परिवर्तित कर सकते हैं। यही है, इससे कोई फर्क नहीं पड़ता कि हस्ताक्षरित चर में कौन सा तार्किक मान संग्रहीत किया जाता है, memcpy गारंटी का उपयोग करके आप मूल हस्ताक्षरित मान वापस प्राप्त कर सकते हैं (क्योंकि प्रकार मानक लेआउट और पर्याप्त आकार दोनों हैं)। –

+3

अधिकतम वास्तव में 255 और 127 हैं। बस कहें '। : पी – TheBuzzSaw

-1

बस चलाने

#include <cstdio> 
#include <stdint.h> 
using namespace std; 

int main() 
{ 
    int64_t a = 5; 
    int64_t aa; 
    uint64_t b; 
    double c; 

    b = *reinterpret_cast<uint64_t *>(&a); 
    aa = *reinterpret_cast<int64_t *>(&b); 

    if (a == aa) { 
     printf("as expected, a == aa\n"); 
    } 

    c = *reinterpret_cast<double *>(&a); 
    aa = *reinterpret_cast<int64_t *>(&c); 

    if (a == aa) { 
     printf("again, as expected, a == aa\n"); 
    } 

    printf("what is this I don't even %f.\n", c); // this one should give some undefined behavior here 

    return 0; 
} 

एक टिप्पणी में फिट नहीं कर सका।

+0

यह काम करने की गारंटी नहीं है, और segfault और अभी भी मानक अनुरूप हो सकता है। 'reinterpret_cast' का उपयोग राउंड-ट्रिप के लिए किया जा सकता है, लेकिन आप मध्यवर्ती को वास्तविक सूचक के रूप में उपयोग नहीं कर सकते, क्योंकि यह कार्यान्वयन परिभाषित किया गया है। _ मुझे किसी भी हार्डवेयर/कंपाइलर के बारे में पता नहीं है, जहां यह काम नहीं करेगा हालांकि –

+0

सबसे अधिक, परिवर्तनीय सी में वैध आईईईई 754 फॉर्म नहीं है। उस बिंदु से, काम करने की कोई गारंटी नहीं है। यद्यपि हस्ताक्षरित/हस्ताक्षरित के लिए भी जाता है, क्योंकि सी ++ इस बारे में कुछ भी नहीं बताता है कि पूर्णांक को कैसे पैक किया जाना चाहिए। – foxx1337

+0

इसके अलावा, यह सख्त एलियासिंग नियम का उल्लंघन करता है (एक पूर्णांक को 'डबल' के रूप में जोड़ना और इसके विपरीत अनुमति नहीं है)। –

1

मुमकिन है यह क्योंकि साइन-परिमाण प्रतिनिधित्व के साथ मशीनों यह है कि अहस्ताक्षरित 0 को 0 नक्शे पर हस्ताक्षर किए कम से कम आश्चर्य की बात है के सिद्धांत का उल्लंघन होगा, जबकि एक पर हस्ताक्षर किए -0 कुछ अन्य (शायद बहुत बड़ी) संख्या के लिए नक्शे होगा के लिए अनुमति नहीं है।

को देखते हुए memcpy समाधान मौजूद है कि मुझे लगता है मानक निकाय इस तरह के एक unintuitive मानचित्रण का समर्थन नहीं करने का फैसला किया, शायद इसलिए कि unsigned-> signed-> अहस्ताक्षरित के रूप में pointer-> integer-> सूचक के रूप में उपयोगी एक दृश्य नहीं है।

+1

एक पल के लिए इन आकार के प्रकारों को अनदेखा करें। मैं कहूंगा कि 'intptr_t' से' uintptr_t' से मैपिंग उपयोगी अनुक्रम होगा। बस विचार करें कि क्या आप एपीआई को गठबंधन करने की कोशिश कर रहे हैं जो कि अलग-अलग हस्ताक्षर चुनने के लिए हुआ था। –

-1

जब तक मैं सवाल नहीं समझ पाते, सिर्फ एक अहस्ताक्षरित प्रकार में हस्ताक्षर किए प्रकार डाल दिया और इसके विपरीत फिर से वापस जाने के लिए:

#include <iostream> 

int main() 
{ 
    signed char s = -128; 
    unsigned char u = s; 
    signed char back = u; 

    std::cout << (int)u << std::endl; 
    std::cout << (int)back << std::endl; 

    return 0; 
} 

./a.out 
128 
-128 
+1

प्रश्न मानक द्वारा की गई गारंटी के बारे में है। सभी लोग सामान्य हार्डवेयर पर काम करने के लिए राउंड-ट्रिप की पूरी उम्मीद करते हैं, लेकिन मानक वास्तव में इसकी गारंटी नहीं देता है। यह आश्चर्यजनक है क्योंकि यदि आप इसे 'memcpy' के साथ करते हैं तो राउंड-ट्रिप * * काम करने की गारंटी है। –

+0

आह हाँ, अब मैं देखता हूं। शोर के लिए खेद है। दिलचस्प सवाल, मैं इस इंप्रेशन के तहत था कि इसकी गारंटी थी। – 01100110

3

हम जानते हैं कि आप फ्लोटिंग प्वाइंट के लिए एक मनमाना बिट अनुक्रम डाली नहीं कर सकता , क्योंकि यह एक जाल प्रतिनिधित्व हो सकता है।

क्या कोई नियम है जो कहता है कि हस्ताक्षरित अभिन्न प्रकारों में जाल प्रतिनिधित्व नहीं हो सकते हैं? (बिना हस्ताक्षर किए गए प्रकार, सीमा निर्धारित करने के तरीके के कारण नहीं हो सकते हैं, वैध मानों के लिए सभी प्रतिनिधित्व की आवश्यकता है)

हस्ताक्षरित प्रस्तुतियों में समकक्ष वर्ग (जैसे +0 == -0) भी शामिल हो सकते हैं और इस तरह के वर्ग में मूल्यों को वाणिज्य में जोड़ सकते हैं कैनोलिक प्रतिनिधित्व, इस प्रकार roundtrip तोड़ने।

यहाँ स्टैंडर्ड से संबंधित नियमों है (4.7, [conv.integral] sectin):

तो गंतव्य प्रकार अहस्ताक्षरित है, जिसके परिणामस्वरूप मूल्य कम से कम अहस्ताक्षरित पूर्णांक स्रोत पूर्णांक के सर्वांगसम है (सापेक्ष 2 n जहां एन हस्ताक्षरित प्रकार का प्रतिनिधित्व करने के लिए उपयोग की जाने वाली बिट्स की संख्या है)। [नोट: दो के पूरक प्रतिनिधित्व में, यह रूपांतरण वैचारिक है और बिट पैटर्न में कोई बदलाव नहीं है (यदि कोई छंटनी नहीं है)। - अंत नोट]

यदि गंतव्य प्रकार हस्ताक्षरित है, तो मान अपरिवर्तित है यदि इसे गंतव्य प्रकार (और बिट-फील्ड चौड़ाई) में प्रदर्शित किया जा सकता है; अन्यथा, मान कार्यान्वयन-परिभाषित है।

आप एक सूचक या संदर्भ पर reinterpret_cast का उपयोग कर मतलब हैं, बल्कि मूल्य से, आप the strict-aliasing rule से निपटने के लिए। और आपको क्या लगता है कि this case is expressly allowed है।

+0

हम्म, उस तर्क से आप फ्लोटिंग-पॉइंट मान को 'memcpy' करने में सक्षम नहीं होना चाहिए, क्योंकि यह जाल सकता है। क्या हस्ताक्षरित/हस्ताक्षरित अभिन्न प्रकारों के बीच शायद memcmpy वास्तव में गारंटी नहीं है? –

+0

@ edA-qamort-ora-y: 'memcpy' कभी भी जाल नहीं करेगा, क्योंकि यह इसके तर्कों की व्याख्या नहीं करता है। लेकिन परिणामी मूल्य का उपयोग करना एक समस्या हो सकती है। –

+0

क्या आप सहमत हैं कि 'memcpy' के माध्यम से एक राउंड-ट्रिप करने से मूल मूल्य को बनाए रखने की गारंटी है? –

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

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