2008-09-08 8 views
103

मान लीजिए मेरे पास निम्न सी कोड है।सी में हस्ताक्षरित रूपांतरण पर हस्ताक्षर किए - क्या यह हमेशा सुरक्षित है?

unsigned int u = 1234; 
int i = -5678; 

unsigned int result = u + i; 

क्या निहित रूपांतरण यहाँ पर जा रहे हैं, और इस कोड u और i के सभी मानों के लिए सुरक्षित है? (सुरक्षित, इस अर्थ में कि इस उदाहरण में भले ही परिणाम कुछ बहुत बड़ा सकारात्मक संख्या के लिए अतिप्रवाह होगा, मैं इसे वापस एक पूर्णांक लिए डाली सकता है और पाने के वास्तविक परिणाम।)

उत्तर

176

लघु उत्तर

आपका iएक अहस्ताक्षरित पूर्णांक के लिए परिवर्तित हो जाएगा UINT_MAX + 1 जोड़कर, तो इसके बाहर अहस्ताक्षरित मूल्यों के साथ, किया जाएगा एक बड़ी result (u और i के मूल्यों के आधार पर) हो जाती है।

6.3.1.8 सामान्य गणित रूपांतरण

  1. दोनों ऑपरेंड एक ही प्रकार है, तो आगे कोई रूपांतरण की जरूरत है:

    लांग उत्तर

    C99 स्टैंडर्ड के अनुसार

  2. अन्यथा, यदि दोनों ऑपरेटरों ने पूर्णांक प्रकारों पर हस्ताक्षर किए हैं या दोनों में निरंतर पूर्णांक प्रकार हैं, तो कम पूर्णांक रूपांतरण रैंक के प्रकार के साथ ऑपरेंड को अधिक रैंक वाले ऑपरेंड के प्रकार में परिवर्तित किया जाता है।
  3. अन्यथा, यदि ऑपरेंड जो पूर्णांक प्रकार के हस्ताक्षर किए गए हैं, तो दूसरे ऑपरेंड के प्रकार के रैंक के बराबर या बराबर रैंक होता है, तो हस्ताक्षरित पूर्णांक प्रकार के साथ ऑपरेंड को बिना हस्ताक्षरित पूर्णांक प्रकार के ऑपरेंड के प्रकार में परिवर्तित किया जाता है।
  4. अन्यथा, यदि हस्ताक्षरित पूर्णांक प्रकार वाले ऑपरेंड का प्रकार ऑपरेट किए गए पूर्णांक प्रकार के साथ ऑपरेंड के प्रकार के सभी मानों का प्रतिनिधित्व कर सकता है, तो हस्ताक्षरित पूर्णांक प्रकार वाले ऑपरेंड को हस्ताक्षर पूर्णांक के साथ ऑपरेंड के प्रकार में परिवर्तित किया जाता है प्रकार।
  5. अन्यथा, दोनों ऑपरेटरों को हस्ताक्षरित पूर्णांक प्रकार के साथ ऑपरेंड के प्रकार से संबंधित हस्ताक्षरित पूर्णांक प्रकार में परिवर्तित कर दिया जाता है।

आपके मामले में, हम एक अहस्ताक्षरित int (u) और हस्ताक्षर किए पूर्णांक (i) है। ऊपर (3) का जिक्र करते हुए, क्योंकि दोनों ऑपरेटरों के पास एक ही रैंक है, आपके i को को एक हस्ताक्षरित पूर्णांक में परिवर्तित करने की आवश्यकता होगी।

6.3.1.3 हस्ताक्षरित और अहस्ताक्षरित पूर्णांकों

  1. जब पूर्णांक प्रकार के साथ एक मूल्य के एक अन्य पूर्णांक _Bool के अलावा अन्य प्रकार में बदल जाती है, मूल्य नए प्रकार द्वारा दर्शाया जा सकता है, यह अपरिवर्तित है।
  2. अन्यथा, यदि नया प्रकार हस्ताक्षरित है, तो मूल्य को अधिकतम प्रकार से अधिक जोड़कर या घटाकर परिवर्तित किया जाता है जिसे नए प्रकार में प्रदर्शित किया जा सकता है जब तक कि मान नए प्रकार की सीमा में न हो।
  3. अन्यथा, नया प्रकार हस्ताक्षरित है और इसमें मूल्य का प्रतिनिधित्व नहीं किया जा सकता है; या तो परिणाम कार्यान्वयन-परिभाषित या कार्यान्वयन-परिभाषित सिग्नल उठाया गया है।

अब हमें ऊपर (2) संदर्भित करने की आवश्यकता है। आपके i को UINT_MAX + 1 जोड़कर एक हस्ताक्षरित मान में परिवर्तित कर दिया जाएगा। तो नतीजा इस बात पर निर्भर करेगा कि आपके कार्यान्वयन पर UINT_MAX कैसे परिभाषित किया गया है। यह बड़ा हो जाएगा, लेकिन यह अतिप्रवाह नहीं होगा, क्योंकि:

6.2.5 (9)

अहस्ताक्षरित ऑपरेंड से जुड़े एक गणना कर सकते हैं कभी नहीं अतिप्रवाह, क्योंकि एक परिणाम है कि जिसके परिणामस्वरूप अहस्ताक्षरित पूर्णांक द्वारा प्रतिनिधित्व नहीं किया जा सकता प्रकार को मॉड्यूलो को कम किया जाता है जो कि सबसे बड़ा मान से अधिक होता है जिसे परिणामी प्रकार से दर्शाया जा सकता है।

बोनस: अंकगणित रूपांतरण अर्द्ध WTF

#include <stdio.h> 

int main(void) 
{ 
    unsigned int plus_one = 1; 
    int minus_one = -1; 

    if(plus_one < minus_one) 
    printf("1 < -1"); 
    else 
    printf("boring"); 

    return 0; 
} 

आप इस ऑनलाइन की कोशिश करने के लिए इस लिंक का उपयोग कर सकते http://codepad.org/yPhYCMFO

बोनस: अंकगणित रूपांतरण साइड इफेक्ट

अंकगणितके मान प्राप्त करने के लिए रूपांतरण नियमों का उपयोग किया जा सकता है 0 यानी -1 को एक अहस्ताक्षरित मूल्य आरंभ, द्वारा:

unsigned int umax = -1; // umax set to UINT_MAX 

इसका कारण यह है कि ऊपर वर्णित रूपांतरण नियमों की एक व्यवस्था के हस्ताक्षर किए संख्या प्रतिनिधित्व की परवाह किए बिना पोर्टेबल होने की गारंटी है। अधिक जानकारी के लिए यह SO प्रश्न देखें: Is it safe to use -1 to set all bits to true?

+11

वहाँ वाह। हस्ताक्षरित से हस्ताक्षरित होने के लिए यह अच्छी तरह से परिभाषित किया गया है, लेकिन हस्ताक्षरित हस्ताक्षर से हस्ताक्षर करने के लिए कार्यान्वयन-परिभाषित किया गया है। – rlbond

+5

यह सही नहीं है। एक भाषा दृष्टिकोण से 'int' से' unsigned int 'में पूर्णांक रूपांतरण में स्रोत ऑब्जेक्ट के मूल्य और कुछ भी (अवधारणात्मक रूप से) इसके आंतरिक प्रतिनिधित्व के साथ सबकुछ करना है। मान मॉड्यूलो 2^एन अंकगणित का उपयोग करके परिवर्तित किया जाता है जहां एन 'हस्ताक्षर किए गए int' में मूल्य बिट्स की संख्या है जो 'int' के लिए कार्यान्वयन उपयोग का प्रतिनिधित्व करता है। –

+0

यह उत्तर बस गलत है। यह समझाता है कि सामान्य कार्यान्वयन कैसे काम करते हैं, न कि भाषा कैसे काम करती है। –

3

जब कोई हस्ताक्षरित और एक हस्ताक्षरित चर जोड़ा जाता है (या कोई बाइनरी ऑपरेशन) दोनों को पूरी तरह से हस्ताक्षरित रूपांतरित कर दिया जाता है, तो इस मामले में परिणामस्वरूप एक बड़ा परिणाम होगा।

तो यह समझ में सुरक्षित है कि नतीजा बड़ा और गलत हो सकता है, लेकिन यह कभी दुर्घटनाग्रस्त नहीं होगा।

+0

सत्य नहीं है। * 6.3.1.8 सामान्य अंकगणितीय रूपांतरण * यदि आप एक int और एक हस्ताक्षरित चार को जोड़ते हैं तो बाद वाले को int में परिवर्तित कर दिया जाता है। यदि आप दो हस्ताक्षरित चार योग करते हैं तो उन्हें int में परिवर्तित कर दिया जाता है। – 2501

3

हस्ताक्षरित हस्ताक्षर किए जाने पर कनवर्ट करने पर दो संभावनाएं होती हैं। संख्याएं जो मूल रूप से सकारात्मक थीं (या एक ही मूल्य के रूप में व्याख्या की जाती हैं) रहती हैं। मूल रूप से नकारात्मक संख्या अब बड़ी सकारात्मक संख्या के रूप में व्याख्या की जाएगी।

1

जैसा कि पहले उत्तर दिया गया था, आप किसी समस्या के बिना हस्ताक्षरित और हस्ताक्षरित के बीच आगे और आगे कास्ट कर सकते हैं। हस्ताक्षरित पूर्णांक के लिए सीमा मामला -1 (0xFFFFFFFF) है। उस से जोड़ने और घटाने का प्रयास करें और आप पाएंगे कि आप वापस आ सकते हैं और यह सही हो सकते हैं।

हालांकि, अगर आप आगे और पीछे कास्टिंग जा रहे हैं, मैं दृढ़ता से अपने चर नामकरण सलाह देंगे ऐसी है कि यह स्पष्ट है कि वे किस प्रकार कर रहे हैं, उदाहरण के लिए:

int iValue, iResult; 
unsigned int uValue, uResult; 

यह प्राप्त करने के लिए अभी तक बहुत आसान है अधिक महत्वपूर्ण मुद्दों से विचलित हो जाते हैं और भूल जाते हैं कि कौन सा चर है, अगर उन्हें संकेत के बिना नाम दिया गया है। आप एक हस्ताक्षरित करने के लिए नहीं डालना चाहते हैं और फिर एक सरणी सूचकांक के रूप में उपयोग करें।

3

the bible का जिक्र करते हुए:

  • आपका अलावा आपरेशन पूर्णांक एक अहस्ताक्षरित int करने के लिए परिवर्तित किया कारण बनता है।
  • दो के पूरक प्रतिनिधित्व और समान रूप से आकार के मानते हुए, थोड़ा पैटर्न बदलता नहीं है।
  • हस्ताक्षरित int से हस्ताक्षरित int से रूपांतरण कार्यान्वयन निर्भर है। (लेकिन यह संभवतः आजकल अधिकांश प्लेटफ़ॉर्म पर आपके द्वारा अपेक्षित तरीके से काम करता है।)
  • नियम अलग-अलग आकारों के हस्ताक्षरित और हस्ताक्षरित संयोजन के मामले में थोड़ा अधिक जटिल हैं।
  • से
16

रूपांतरण अहस्ताक्षरित पर हस्ताक्षर किए नहीं जरूरी बस कॉपी या हस्ताक्षरित मूल्य का प्रतिनिधित्व पुनर्व्याख्या करता है। सी मानक (C99 6.3.1.3) का हवाला देते हुए:

जब पूर्णांक प्रकार के साथ एक मूल्य के एक अन्य पूर्णांक _Bool के अलावा अन्य प्रकार में बदल जाती है, अगर मूल्य नए प्रकार द्वारा दर्शाया जा सकता है, यह अपरिवर्तित है।

अन्यथा, यदि नए प्रकार अहस्ताक्षरित है, मूल्य बार-बार जोड़ने या अधिकतम मूल्य है कि नए प्रकार में व्यक्त किया जा सकता जब तक मूल्य नए प्रकार की सीमा में है एक से अधिक घटा कर बदल जाती है।

अन्यथा, नया प्रकार हस्ताक्षरित है और मूल्य में इसका प्रतिनिधित्व नहीं किया जा सकता है; या तो परिणाम कार्यान्वयन-परिभाषित या कार्यान्वयन-परिभाषित संकेत उठाया गया है।

इन दिनों के पूरक प्रतिनिधित्व के लिए इन दिनों लगभग सार्वभौमिक है, नियम बिट्स को दोबारा परिभाषित करने के अनुरूप हैं। लेकिन अन्य प्रस्तुतियों (साइन-एंड-परिमाण या किसी के पूरक) के लिए, सी कार्यान्वयन को अभी भी एक ही परिणाम की व्यवस्था करनी होगी, जिसका अर्थ है कि रूपांतरण केवल बिट्स की प्रतिलिपि नहीं बना सकता है। उदाहरण के लिए, (हस्ताक्षरित) -1 == UINT_MAX, प्रतिनिधित्व के बावजूद।

सामान्य रूप से, सी में रूपांतरणों को मानों पर काम करने के लिए परिभाषित किया जाता है, न कि प्रतिनिधित्व पर।

unsigned int u = 1234; 
int i = -5678; 

unsigned int result = u + i; 

मैं का मूल्य अहस्ताक्षरित पूर्णांक में बदल जाती है, UINT_MAX + 1 - 5678 उपज:

मूल प्रश्न का उत्तर देने। यह मान तब हस्ताक्षर किए गए मान 1234 में जोड़ा जाता है, जो UINT_MAX + 1 - 4444 प्रदान करता है।

(अहस्ताक्षरित अतिप्रवाह के विपरीत, पर हस्ताक्षर किए अतिप्रवाह अपरिभाषित व्यवहार का आह्वान Wraparound आम है, लेकिन सी मानक द्वारा इसकी गारंटी नहीं है -। और संकलक अनुकूलन कोड अनुचित धारणाएं बनाता है कि पर कहर ढ़ा सकते हैं।)

-15

भयानक जवाब गैलोर

Ozgur Ozcitak

जब आप अहस्ताक्षरित पर हस्ताक्षर किए से डाली (और इसके विपरीत) आंतरिक संख्या का प्रतिनिधित्व परिवर्तन नहीं है।क्या परिवर्तन है कंपाइलर साइन बिट की व्याख्या कैसे करता है।

यह पूरी तरह से गलत है।

मैट Fredriksson

जब एक अहस्ताक्षरित और एक पर हस्ताक्षर किए चर जोड़ रहे हैं (या किसी भी बाइनरी आपरेशन) दोनों, परोक्ष अहस्ताक्षरित करने के लिए परिवर्तित कर रहे हैं जो में ही किसी विशाल परिणाम में इस मामले परिणाम ।

यह भी गलत है। बिना हस्ताक्षर किए गए इनट्स को इन्ट्स को पदोन्नत किया जा सकता है, चाहे वे बिना हस्ताक्षर किए गए प्रकार में पैडिंग बिट्स के कारण समान सटीक हों।

smh

आपका अलावा आपरेशन पूर्णांक एक अहस्ताक्षरित int करने के लिए परिवर्तित किया कारण बनता है।

गलत। शायद यह करता है और शायद यह नहीं करता है।

हस्ताक्षर किए गए int से हस्ताक्षर किए गए हस्ताक्षर int रूपांतरण कार्यान्वयन पर निर्भर है। (लेकिन यह शायद जिस तरह से आप उम्मीद सबसे इन दिनों प्लेटफार्मों पर काम करता है।)

गलत। यह या तो अपरिभाषित व्यवहार है यदि यह ओवरफ्लो का कारण बनता है या मान संरक्षित होता है।

बेनामी

मैं का मूल्य अहस्ताक्षरित पूर्णांक में बदल जाती है ...

गलत। एक हस्ताक्षरित int के सापेक्ष एक int की परिशुद्धता पर निर्भर करता है।

टेलर मूल्य

जैसा कि पहले उत्तर दिया गया है, तो आप वापस आगे एक समस्या पर भी कास्ट और अहस्ताक्षरित पर हस्ताक्षर किए के बीच और कर सकते हैं।

गलत। अपरिभाषित व्यवहार में एक हस्ताक्षरित पूर्णांक परिणामों की सीमा के बाहर एक मूल्य को स्टोर करने का प्रयास कर रहा है।

अब मैं अंततः प्रश्न का उत्तर दे सकता हूं।

int की परिशुद्धता को हस्ताक्षरित int के बराबर होना चाहिए, आपको एक हस्ताक्षरित int में पदोन्नत किया जाएगा और आपको अभिव्यक्ति (u + i) से मूल्य -4444 प्राप्त होगा। अब, क्या आपके पास और मेरे पास अन्य मूल्य हैं, आप अतिप्रवाह और अपरिभाषित व्यवहार प्राप्त कर सकते हैं लेकिन उन सटीक संख्याओं के साथ आपको -4444 [1] मिलेगा। इस मान में टाइप int होगा।लेकिन आप उस मान को एक हस्ताक्षरित int में संग्रहीत करने की कोशिश कर रहे हैं ताकि उसके बाद एक हस्ताक्षरित int और मूल्य जो परिणाम समाप्त हो जाएगा (UINT_MAX + 1) - 4444.

को हस्ताक्षर किए जाने की सटीकता int int की तुलना में अधिक हो, हस्ताक्षरित int को मूल्य (UINT_MAX + 1) - 5678 उत्पन्न करने वाले एक हस्ताक्षरित int को बढ़ावा दिया जाएगा जो अन्य हस्ताक्षरित int 1234 में जोड़ा जाएगा। क्या आपके और मेरे पास अन्य मान हैं, जो आपको बनाना चाहिए अभिव्यक्ति {0..UINT_MAX} रेंज के बाहर गिरती है (UINT_MAX + 1) या तो तब तक जोड़ा या घटाया जाएगा जब तक कि परिणाम {0..UINT_MAX) के अंदर न हो जाए और कोई अपरिभाषित व्यवहार न हो।

सटीकता क्या है?

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

[Gotchas]

मैक्रो sizeof मैक्रो अकेले एक पूर्णांक की शुद्धता निर्धारित करने के लिए करता है, तो गद्दी बिट्स मौजूद हैं नहीं किया जा सकता। और एक बाइट का आकार सी 99 द्वारा परिभाषित एक ऑक्टेट (आठ बिट्स) होना आवश्यक नहीं है।

[1] अतिप्रवाह दो बिंदुओं में से एक पर हो सकता है। इसके अलावा (प्रचार के दौरान) - जब आपके पास एक हस्ताक्षरित int है जो int के अंदर फिट होने के लिए बहुत बड़ा है। ओवरफ्लो भी जोड़ के बाद भी हो सकता है भले ही बिना हस्ताक्षर किए int int int की सीमा के भीतर था, इसके परिणामस्वरूप परिणाम अभी भी अतिप्रवाह हो सकता है।


एक असंबंधित नोट पर, मैं हाल ही में एक स्नातक काम खोजने की कोशिश कर छात्र हूँ;)

+6

"बिना हस्ताक्षर किए गए इंट्स को इन्ट्स में प्रचारित किया जा सकता है"। सच नहीं। कोई पूर्णांक _promotion_ तब होता है क्योंकि प्रकार पहले से ही रैंक> = int हैं। 6.3.1.1: "किसी भी हस्ताक्षरित पूर्णांक प्रकार का रैंक इसी तरह के हस्ताक्षरित पूर्णांक प्रकार के रैंक के बराबर होगा, यदि कोई हो।" और 6.3.1.8: "अन्यथा, यदि ऑपरेंड जिसने पूर्णांक प्रकार को हस्ताक्षर नहीं किया है, तो दूसरे ऑपरेंड के प्रकार के रैंक पर अधिक ** या बराबर ** रैंक है, तो हस्ताक्षरित पूर्णांक प्रकार के साथ ऑपरेंड को प्रकार के रूप में परिवर्तित किया जाता है हस्ताक्षरित पूर्णांक प्रकार के साथ ऑपरेट करें। " दोनों गारंटी देते हैं कि सामान्य अंकगणितीय रूपांतरण लागू होने पर 'int' को 'हस्ताक्षरित int' में परिवर्तित कर दिया जाता है। –

+1

6.3.1.8 पूर्णांक पदोन्नति के बाद ही होता है। अनुच्छेद खोलना कहता है "अन्यथा, पूर्णांक प्रचार दोनों ऑपरेटरों पर किया जाता है। फिर निम्नलिखित नियम प्रचारित ऑपरेटरों पर लागू होते हैं"। तो पदोन्नति नियमों को पढ़ें 6.3.1.1 ... "एक पूर्णांक प्रकार के साथ एक ऑब्जेक्ट या अभिव्यक्ति जिसका पूर्णांक रूपांतरण रैंक कम है या int और unsigned int के रैंक के लिए EQUAL" और "यदि कोई int सभी मानों का प्रतिनिधित्व कर सकता है मूल प्रकार, मान int में परिवर्तित हो जाता है "। –

+1

6.3.1.1 इंटीजर पदोन्नति का उपयोग कुछ पूर्णांक प्रकारों को परिवर्तित करने के लिए किया जाता है जो उन प्रकारों में से किसी एक को 'int' या' हस्ताक्षरित int 'नहीं होते हैं, जहां कुछ प्रकार के' हस्ताक्षरित int 'या' int' की अपेक्षा की जाती है। "या बराबर" टीसी 2 में जोड़ा गया था ताकि समेकित प्रकार के रूपांतरण रैंक को 'int' या 'unsigned int' के बराबर उन प्रकारों में परिवर्तित किया जा सके। इसका इरादा कभी नहीं था कि वर्णित पदोन्नति 'हस्ताक्षरित int' और 'int' के बीच परिवर्तित हो जाएगी। 'हस्ताक्षरित int' और 'int' के बीच सामान्य प्रकार निर्धारण अभी भी 6.3.1.8 तक शासित है, यहां तक ​​कि टीसी 2 भी पोस्ट किया गया है। –

0

क्या निहित रूपांतरण यहाँ पर जा रहे हैं,

मैं एक में परिवर्तित हो जाएगा हस्ताक्षरित पूर्णांक।

और क्या यह कोड आपके और मेरे सभी मूल्यों के लिए सुरक्षित है?

अच्छी तरह से परिभाषित हां होने के अर्थ में सुरक्षित (https://stackoverflow.com/a/50632/5083516 देखें)।

नियम मानक मानने के लिए आम तौर पर कठिन लिखा जाता है लेकिन अनिवार्य रूप से हस्ताक्षर किए गए पूर्णांक में जो भी प्रतिनिधित्व किया गया था, हस्ताक्षर किए गए पूर्णांक में संख्या का 2 पूरक पूरक होगा।

जोड़, घटाव और गुणा इन संख्याओं पर सही ढंग से काम करेगा जिसके परिणामस्वरूप एक और हस्ताक्षरित पूर्णांक होता है जिसमें "वास्तविक परिणाम" का प्रतिनिधित्व करने वाले एक जुड़वां पूरक संख्या होती है।

बड़े हस्ताक्षरित पूर्णांक प्रकारों के विभाजन और कास्टिंग में अच्छी तरह से परिभाषित परिणाम होंगे लेकिन वे परिणाम "वास्तविक परिणाम" के 2 पूरक पूरक नहीं होंगे।

(सुरक्षित, इस अर्थ में कि इस उदाहरण में परिणाम कुछ बड़े सकारात्मक संख्या में बह जाएगा, मैं इसे वापस एक int में डाल सकता हूं और वास्तविक परिणाम प्राप्त कर सकता हूं।)

से होने वाले रूपांतरण अहस्ताक्षरित पर हस्ताक्षर किए मानक रिवर्स द्वारा परिभाषित कर रहे हैं कार्यान्वयन से परिभाषित दोनों जीसीसी और MSVC रूपांतरण ऐसी है कि आप "वास्तविक परिणाम" जब रूपांतरित होने में 2 के पूरक नंबर एक में संग्रहीत हो जाएगा परिभाषित एक हस्ताक्षरित पूर्णांक पर हस्ताक्षरित पूर्णांक वापस। मुझे आशा है कि आपको केवल अस्पष्ट सिस्टम पर कोई अन्य व्यवहार मिलेगा जो हस्ताक्षर किए गए पूर्णांक के लिए 2 के पूरक का उपयोग नहीं करता है।

https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation https://msdn.microsoft.com/en-us/library/0eex498h.aspx

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