हस्ताक्षरित / अहस्ताक्षरित तुलना


85

मैं यह समझने की कोशिश कर रहा हूं कि संकेतित स्थान पर निम्न कोड चेतावनी क्यों जारी नहीं करता है।

//from limits.h
#define UINT_MAX 0xffffffff /* maximum unsigned int value */
#define INT_MAX  2147483647 /* maximum (signed) int value */
            /* = 0x7fffffff */

int a = INT_MAX;
//_int64 a = INT_MAX; // makes all warnings go away
unsigned int b = UINT_MAX;
bool c = false;

if(a < b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a > b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a <= b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a >= b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a == b) // no warning <--- warning expected here
    c = true;
if(((unsigned int)a) == b) // no warning (as expected)
    c = true;
if(a == ((int)b)) // no warning (as expected)
    c = true;

मुझे लगा कि यह पृष्ठभूमि के प्रचार के साथ करना है, लेकिन अंतिम दो अन्यथा कहने लगते हैं।

मेरे दिमाग में, पहली ==तुलना दूसरों के समान ही एक हस्ताक्षरित / अहस्ताक्षरित बेमेल है?


3
gcc 4.4.2 प्रिंट को चेतावनी देता है जब '-Wall' के साथ आह्वान किया जाता है
bobah

यह अटकलें हैं, लेकिन शायद सभी तुलनाओं का अनुकूलन कर रहा है क्योंकि यह संकलन समय पर जवाब जानता है।
Null सेट

2
आह! कर रहे हैं। bobah की टिप्पणी: मैंने सभी चेतावनियों को चालू कर दिया है और लापता चेतावनी अब दिखाई देती है। मेरा विचार है कि इसे अन्य वार्ताओं की तरह ही चेतावनी स्तर पर दिखाई देना चाहिए था।
पीटर

1
@bobah: मुझे वास्तव में नफरत है कि 4.4.2 gcc उस चेतावनी को प्रिंट करता है (यह बताने का कोई तरीका नहीं है कि इसे केवल असमानता के लिए छापना है), क्योंकि चेतावनी देने के सभी तरीके मौन रखते हैं, जिससे चीजें बदतर हो जाती हैं । डिफ़ॉल्ट पदोन्नति मज़बूती से -1 या ~ 0 दोनों को किसी भी अहस्ताक्षरित प्रकार के उच्चतम संभव मान में कनवर्ट करता है, लेकिन यदि आप इसे स्वयं कास्टिंग करके चेतावनी को चुप करते हैं, तो आपको सटीक प्रकार जानना होगा । इसलिए यदि आप प्रकार बदलते हैं (इसे लंबे समय तक बिना बताए बढ़ाएँ), तो आपकी तुलना में नंगे -1काम (लेकिन वे चेतावनी देते हैं), जबकि आपकी तुलना -1uया (unsigned)-1दोनों बुरी तरह से विफल हो जाएंगे।
Jan Hudec

मुझे नहीं पता कि आपको चेतावनी की आवश्यकता क्यों है, और संकलक सिर्फ इसे काम क्यों नहीं कर सकते हैं। -1 नकारात्मक है इसलिए किसी भी अहस्ताक्षरित संख्या से कम है। साधारण।
कैशोकॉव

जवाबों:


95

जब अहस्ताक्षरित के साथ हस्ताक्षरित तुलना, संकलक हस्ताक्षरित मूल्य को अहस्ताक्षरित में परिवर्तित करता है। समानता के लिए, यह कोई फर्क नहीं पड़ता, -1 == (unsigned) -1। अन्य तुलनाओं के लिए यह मायने रखता है, उदाहरण के लिए निम्न सत्य है -1 > 2U:।

संपादित करें: संदर्भ:

5/9: (भाव)

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

  • यदि या तो ऑपरेंड टाइप डबल का है, तो दूसरा लॉन्ग डबल में बदल जाएगा।

  • अन्यथा, यदि कोई ऑपरेंड डबल है, तो दूसरे को डबल में बदल दिया जाएगा।

  • अन्यथा, यदि कोई ऑपरेंड फ्लोट है, तो दूसरे को फ्लोट में बदल दिया जाएगा।

  • अन्यथा, अभिन्न पदोन्नति (4.5) दोनों ऑपरेंड पर किया जाएगा ।.54)

  • फिर, यदि किसी भी ऑपरेंड को लंबे समय तक अनसाइन किया जाता है, तो दूसरे को लंबे समय के लिए बदल दिया जाएगा।

  • अन्यथा, यदि एक ऑपरेंड एक लंबा इंट और दूसरा अहस्ताक्षरित इंट है, तो यदि एक लंबा इंट एक अहस्ताक्षरित इंट के सभी मूल्यों का प्रतिनिधित्व कर सकता है, तो अहस्ताक्षरित इंट एक लंबे इंट में परिवर्तित हो जाएगा; अन्यथा दोनों ऑपरेंड्स को अहस्ताक्षरित लंबे इंट में परिवर्तित किया जाएगा।

  • अन्यथा, यदि कोई ऑपरेंड लंबा है, तो दूसरे को लंबे समय तक परिवर्तित किया जाएगा।

  • अन्यथा, यदि कोई ऑपरेंड अहस्ताक्षरित है, तो अन्य को अहस्ताक्षरित में बदल दिया जाएगा।

4.7 / 2: (एकात्म रूपांतरण)

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

EDIT2: MSVC चेतावनी स्तर

MSVC के विभिन्न चेतावनी स्तरों के बारे में चेतावनी दी गई है, ज़ाहिर है, डेवलपर्स द्वारा किए गए विकल्प। जैसा कि मैं इसे देखता हूं, हस्ताक्षरित / अहस्ताक्षरित समानता बनाम अधिक / कम तुलनाओं के संबंध में उनके विकल्प समझ में आते हैं, यह पूरी तरह से निश्चित रूप से विषय है:

-1 == -1इसका मतलब वही है -1 == (unsigned) -1- मुझे लगता है कि एक सहज परिणाम है।

-1 < 2 इसका मतलब यह नहीं है -1 < (unsigned) 2- यह पहली नज़र में कम सहज है, और आईएमओ "पहले" चेतावनी के योग्य है।


आप हस्ताक्षर किए को अहस्ताक्षरित में कैसे परिवर्तित कर सकते हैं? हस्ताक्षरित मूल्य -1 का अहस्ताक्षरित संस्करण क्या है? (हस्ताक्षर किए -1 = 1111, जबकि अहस्ताक्षरित 15 = 1111, बिटवाइज़ वे समान हो सकते हैं, लेकिन वे तार्किक रूप से समान नहीं हैं।) मैं समझता हूं कि यदि आप इस रूपांतरण को बल देते हैं तो यह काम करेगा, लेकिन संकलक ऐसा क्यों करेगा? यह अतार्किक है। इसके अलावा, जैसा कि मैंने ऊपर टिप्पणी की थी, जब मैंने चेतावनियों को गायब कर दिया था == चेतावनी दिखाई दी, जो मुझे लगता है कि क्या कहती है?
पीटर

1
जैसा कि 4.7 / 2 कहते हैं, अहस्ताक्षरित पर हस्ताक्षर का मतलब है दो के पूरक के लिए बिट पैटर्न में कोई बदलाव नहीं। संकलक ऐसा क्यों करता है, यह C ++ मानक द्वारा आवश्यक है। मेरा मानना ​​है कि विभिन्न स्तरों पर वीएस की चेतावनियों के पीछे तर्क अभिव्यक्ति का मौका है - और मैं उनसे सहमत हूं कि हस्ताक्षरित / अहस्ताक्षरित की समानता की तुलना असमानता की तुलना में एक समस्या होने की "कम संभावना" है। यह निश्चित रूप से व्यक्तिपरक है - ये कुलपति संकलक डेवलपर्स द्वारा किए गए विकल्प हैं।
एरिक

ठीक है, मुझे लगता है कि मुझे लगभग मिल गया है। जिस तरह से मैंने पढ़ा है, वह संकलक है (वैचारिक रूप से) कर रहा है: 'अगर ((अहस्ताक्षरित _int64) 0x7fffffff) == ((अहस्ताक्षरित _int64) 0xffffffff)', क्योंकि _int64 सबसे छोटा प्रकार है जो 0x7fffff और 0xffffffff दोनों का प्रतिनिधित्व कर सकता है। अहस्ताक्षरित शब्दों में?
पीटर

2
वास्तव में तुलना करना (unsigned)-1या तुलना करना -1uअक्सर खराब होता है -1। ऐसा इसलिए है (unsigned __int64)-1 == -1, लेकिन (unsigned __int64)-1 != (unsigned)-1। इसलिए यदि संकलक एक चेतावनी देता है, तो आप इसे बिना बताए या उपयोग करके कास्ट करने का प्रयास करते हैं -1uऔर यदि मूल्य वास्तव में 64-बिट होता है या आप इसे बाद में बदलने के लिए होते हैं, तो आप अपना कोड तोड़ देंगे! और याद रखें कि size_tअहस्ताक्षरित है, 64-बिट प्लेटफार्मों पर केवल 64-बिट और अमान्य मान के लिए -1 का उपयोग करना इसके साथ बहुत आम है।
Jan Hudec

1
शायद cpmpilers तब ऐसा नहीं करना चाहिए। यदि यह हस्ताक्षरित और अहस्ताक्षरित की तुलना करता है, तो बस जांचें कि क्या हस्ताक्षरित मूल्य नकारात्मक है। यदि ऐसा है तो यह अहस्ताक्षरित की तुलना में कम होने की गारंटी है।
कैशोकॉव

32

क्यों हस्ताक्षरित / अहस्ताक्षरित चेतावनी महत्वपूर्ण हैं और प्रोग्रामर को उन्हें ध्यान देना चाहिए, निम्न उदाहरण द्वारा प्रदर्शित किया जाता है।

इस कोड का आउटपुट लगता है?

#include <iostream>

int main() {
        int i = -1;
        unsigned int j = 1;
        if ( i < j ) 
            std::cout << " i is less than j";
        else
            std::cout << " i is greater than j";

        return 0;
}

आउटपुट:

i is greater than j

आश्चर्य चकित? ऑनलाइन डेमो: http://www.ideone.com/5iCxY

बॉटमलाइन: तुलना में, यदि एक ऑपरेंड है unsigned, तो दूसरे ऑपरेंड को संक्षेप में बदल दिया जाता है unsigned यदि उसका प्रकार हस्ताक्षरित है!


2
वह सही है! यह गूंगा है, लेकिन वह सही है। यह एक प्रमुख गोच है जो मैं पहले कभी नहीं आया था। यह अहस्ताक्षरित को (बड़े) हस्ताक्षरित मान में परिवर्तित क्यों नहीं करता है ?! यदि आप "if (i <((int) j)))" करते हैं तो यह वैसा ही काम करता है जैसा आप अपेक्षा करते हैं। हालाँकि "if (i <((_int64) j)))" अधिक अर्थ देगा (यह मानते हुए, जिसे आप नहीं कर सकते, कि _int64 इंट का आकार दोगुना है)।
पीटर

6
@Peter "यह असंतुष्ट को (बड़े) हस्ताक्षरित मान में परिवर्तित क्यों नहीं करता है?" उत्तर सरल है: एक बड़ा हस्ताक्षरित मूल्य नहीं हो सकता है। 32 बिट मशीन पर, लंबे समय से पहले के दिनों में, दोनों int और long 32 बिट्स थे, और कुछ भी बड़ा नहीं था। जब हस्ताक्षरित और अहस्ताक्षरित की तुलना करते हैं, तो जल्द से जल्द C ++ संकलक दोनों को हस्ताक्षरित में बदल देता है। मैं भूल जाता हूं कि किन कारणों से, सी मानकों समिति ने इसे बदल दिया। जितना संभव हो उतना अहस्ताक्षरित से बचने के लिए आपका सबसे अच्छा समाधान है।
जेम्स कंज

5
@JamesKanze: मुझे संदेह है कि इस तथ्य के साथ भी कुछ करना है, हस्ताक्षरित अतिप्रवाह का परिणाम अपरिभाषित व्यवहार है, जबकि अहस्ताक्षरित अतिप्रवाह का परिणाम नहीं है और इसलिए अहस्ताक्षरित मूल्य पर हस्ताक्षर किए गए मूल्य को नकारात्मक हस्ताक्षरित के बड़े अहस्ताक्षरित रूपांतरण में परिभाषित किया गया है। मान नहीं है
Jan Hudec

2
@ जेम्स कम्पाइलर हमेशा असेंबली उत्पन्न कर सकता है जो इस तुलना के अधिक सहज शब्दार्थों को कुछ बड़े प्रकार के कास्टिंग के बिना लागू करेगा। इस विशेष उदाहरण में, पहले यह जाँचना पर्याप्त होगा कि क्या i<0। फिर iनिश्चित से छोटा jहै। यदि iशून्य से कम नहीं है, तो ìइसकी तुलना करने के लिए सुरक्षित रूप से अहस्ताक्षरित में परिवर्तित किया जा सकता है j। ज़रूर, हस्ताक्षरित और अहस्ताक्षरित के बीच तुलना धीमी होगी, लेकिन उनका परिणाम कुछ अर्थों में अधिक सही होगा।
स्वेन

@ मैं सहमत हूँ। मानक को दो प्रकारों में से एक में परिवर्तित करने के बजाय सभी वास्तविक मूल्यों के लिए काम करने के लिए तुलना की आवश्यकता हो सकती है। यह केवल तुलना के लिए काम करेगा, हालाँकि; मुझे संदेह है कि समिति तुलनाओं और अन्य कार्यों के लिए अलग नियम नहीं चाहती थी (और तुलनात्मक रूप से निर्दिष्ट करने की समस्या पर हमला नहीं करना चाहती थी जब वास्तव में तुलना नहीं की जा रही थी)।
बजे जेम्स कान्जे

4

== ऑपरेटर बस एक बिटवाइज़ तुलना करता है (साधारण विभाजन द्वारा यह देखने के लिए कि यह 0 है)।

तुलना की तुलना में छोटे / बड़े संख्या के संकेत पर अधिक भरोसा करते हैं।

4 बिट उदाहरण:

1111 = 15? या -1?

इसलिए यदि आपके पास 1111 <0001 है ... यह अस्पष्ट है ...

लेकिन अगर आपके पास 1111 == 1111 है ... यह एक ही बात है, हालांकि आपका मतलब यह नहीं है।


मैं इसे समझता हूं, लेकिन यह मेरे सवाल का जवाब नहीं देता है। जैसा कि आप बताते हैं, 1111! = 1111 यदि संकेत मेल नहीं खाते हैं। संकलक जानता है कि प्रकारों से एक बेमेल है, इसलिए यह इसके बारे में चेतावनी क्यों नहीं देता है? (मेरी बात यह है कि मेरे कोड में कई ऐसे बेमेल शामिल हो सकते हैं, जिनके बारे में मुझे चेतावनी नहीं दी जा रही है।)
पीटर

यह जिस तरह से डिज़ाइन किया गया है। समानता परीक्षण समानता की जाँच करता है। और यह समान है। मैं आपसे सहमत हूं कि यह इस तरह नहीं होना चाहिए। आप एक मैक्रो या कुछ ऐसा कर सकते हैं जो x == y से अधिक हो जाए! (X <y) || (x> y))
Yochai Timmer

1

एक प्रणाली में जो 2-पूरक (सबसे आधुनिक प्रोसेसर) का उपयोग करके मूल्यों का प्रतिनिधित्व करता है वे अपने बाइनरी रूप में भी समान हैं। ऐसा इसलिए हो सकता है कि कंपाइलर == b के बारे में शिकायत न करे ।

और मेरे लिए यह अजीब संकलक है आपको == ((int) b) पर चेतावनी नहीं देता है । मुझे लगता है कि यह आपको पूर्णांक ट्रंकेशन चेतावनी या कुछ और देना चाहिए।


1
C / C ++ का दर्शन है: संकलक को भरोसा है कि डेवलपर जानता है कि वह क्या कर रहा है जब वह स्पष्ट रूप से प्रकारों के बीच परिवर्तित कर रहा है। इस प्रकार, कोई चेतावनी (कम से कम डिफ़ॉल्ट रूप से - मेरा मानना ​​है कि ऐसे कंपाइलर हैं जो इसके लिए चेतावनी उत्पन्न करते हैं यदि चेतावनी का स्तर डिफ़ॉल्ट से अधिक है)।
Péter Török

0

प्रश्न में कोड की लाइन C4018 चेतावनी उत्पन्न नहीं करती है क्योंकि Microsoft ने एक अलग चेतावनी संख्या (यानी C4389) का उपयोग किया है उस मामले को संभालने के लिए ) का , और C4389 डिफ़ॉल्ट रूप से सक्षम नहीं है (अर्थात स्तर 3 पर)।

C4389 के लिए Microsoft डॉक्स से :

// C4389.cpp
// compile with: /W4
#pragma warning(default: 4389)

int main()
{
   int a = 9;
   unsigned int b = 10;
   if (a == b)   // C4389
      return 0;
   else
      return 0;
};

अन्य जवाबों ने यह अच्छी तरह से समझाया है कि Microsoft ने एक विशेष मामले को समानता ऑपरेटर से बाहर करने का फैसला क्यों किया होगा, लेकिन मुझे लगता है कि C4389 का उल्लेख किए बिना या विजुअल स्टूडियो में इसे कैसे सक्षम किया जाए, ये उत्तर सुपर उपयोगी नहीं हैं।

मुझे यह भी उल्लेख करना चाहिए कि यदि आप C4389 को सक्षम करने जा रहे हैं, तो आप C4388 को सक्षम करने पर भी विचार कर सकते हैं। दुर्भाग्य से C4388 के लिए कोई आधिकारिक दस्तावेज नहीं है, लेकिन यह निम्नलिखित की तरह अभिव्यक्ति में पॉप अप लगता है:

int a = 9;
unsigned int b = 10;
bool equal = (a == b); // C4388
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.