C # कंपाइलर इसका अनुवाद क्यों करता है! = तुलना करना मानो यह एक> तुलना थी?


147

मुझे विशुद्ध संभावना से पता चला है कि C # संकलक इस विधि को चालू करता है:

static bool IsNotNull(object obj)
{
    return obj != null;
}

... इस सीआईएल में :

.method private hidebysig static bool IsNotNull(object obj) cil managed
{
    ldarg.0   // obj
    ldnull
    cgt.un
    ret
}

... या, यदि आप विघटित सी # कोड देखना पसंद करते हैं:

static bool IsNotNull(object obj)
{
    return obj > null;   // (note: this is not a valid C# expression)
}

कैसे आए कि !=एक " >" के रूप में अनुवादित हो जाए ?

जवाबों:


201

संक्षिप्त जवाब:

आईएल में कोई "तुलना-नहीं-बराबर" निर्देश है, इसलिए सी # !=ऑपरेटर का कोई सटीक पत्राचार नहीं है और इसका शाब्दिक अनुवाद नहीं किया जा सकता है।

हालांकि, एक "तुलना-बराबर" निर्देश है ( ceq, ==ऑपरेटर के लिए एक सीधा पत्राचार ), इसलिए सामान्य मामले में, x != yइसके थोड़े लंबे समकक्ष की तरह अनुवाद किया जाता है (x == y) == false

है भी एक "की तुलना-अधिक से अधिक" आईएल (में शिक्षा cgt) जो संकलक कुछ शॉर्टकट (यानी उत्पन्न कम आईएल कोड), एक होने अशक्त के खिलाफ वस्तुओं की है कि असमानता तुलना लेने के लिए अनुमति देता है, obj != null, अनुवाद करा रूप में यदि वे थे " obj > null"।

आइए कुछ और विस्तार में जाएं।

यदि IL में कोई "तुलना-नहीं-बराबर" निर्देश है, तो संकलक द्वारा निम्नलिखित विधि का अनुवाद कैसे किया जाएगा?

static bool IsNotEqual(int x, int y)
{
    return x != y;
}

जैसा कि पहले ही ऊपर कहा, संकलक बदल जाएगी x != yमें (x == y) == false:

.method private hidebysig static bool IsNotEqual(int32 x, int32 y) cil managed 
{
    ldarg.0   // x
    ldarg.1   // y
    ceq
    ldc.i4.0  // false
    ceq       // (note: two comparisons in total)
    ret
}

यह पता चला है कि संकलक हमेशा इस काफी लंबे-घुमावदार पैटर्न का उत्पादन नहीं करता है। आइए देखें कि जब हम yनिरंतर 0 से बदलते हैं तो क्या होता है :

static bool IsNotZero(int x)
{
    return x != 0;
}

उत्पादित आईएल सामान्य मामले की तुलना में कुछ कम है:

.method private hidebysig static bool IsNotZero(int32 x) cil managed 
{
    ldarg.0    // x
    ldc.i4.0   // 0
    cgt.un     // (note: just one comparison)
    ret
}

कंपाइलर इस तथ्य का लाभ उठा सकता है कि हस्ताक्षर किए गए पूर्णांक दो के पूरक में संग्रहीत किए जाते हैं (जहां, यदि परिणामी बिट पैटर्न को अहस्ताक्षरित पूर्णांक के रूप में व्याख्या किया जाता है - यही .unइसका मतलब है - 0 का सबसे छोटा संभव मूल्य है), इसलिए यह अनुवाद x == 0करता है जैसे कि यहunchecked((uint)x) > 0

यह पता चलता है कि कंपाइलर असमानता की जांच के लिए सिर्फ इतना ही कर सकता है null:

static bool IsNotNull(object obj)
{
    return obj != null;
}

संकलक लगभग उसी आईएल का उत्पादन करता है जैसे IsNotZero:

.method private hidebysig static bool IsNotNull(object obj) cil managed 
{
    ldarg.0
    ldnull   // (note: this is the only difference)
    cgt.un
    ret
}

जाहिरा तौर पर, कंपाइलर को यह मानने की अनुमति है कि बिट पैटर्न null संदर्भ का बिट पैटर्न किसी भी वस्तु संदर्भ के लिए संभव सबसे छोटा बिट पैटर्न है।

यह शॉर्टकट कॉमन लैंग्वेज इन्फ्रास्ट्रक्चर एनोटेट स्टैंडर्ड (अक्टूबर 2003 से प्रथम संस्करण) में स्पष्ट रूप से उल्लेख किया गया है (पृष्ठ 491 पर, टेबल 6-4 के फुटनोट के रूप में, "बाइनरी तुलना या शाखा संचालन"):

" cgt.unObjectRefs (O) पर अनुमति दी गई है और पुष्टि की गई है। इसका उपयोग आमतौर पर अशक्त ऑब्जेक्ट के साथ null की तुलना करते समय किया जाता है (कोई तुलना नहीं-बराबर" निर्देश है, जो अन्यथा अधिक स्पष्ट समाधान होगा)। "


3
उत्कृष्ट उत्तर, सिर्फ एक नाइट: दो का पूरक यहां प्रासंगिक नहीं है। यह केवल मायने रखता है कि हस्ताक्षर किए गए पूर्णांकों को इस तरह संग्रहीत किया जाता है कि गैर-नकारात्मक मानों intका वैसा ही प्रतिनिधित्व हो intजैसा वे करते हैं uint। यह दो के पूरक की तुलना में बहुत कमजोर आवश्यकता है।

3
अहस्ताक्षरित प्रकारों में कभी भी कोई ऋणात्मक संख्या नहीं होती है, इसलिए एक तुलना ऑपरेशन जो शून्य की तुलना करता है, किसी भी शून्य संख्या को शून्य से कम नहीं मान सकता है। गैर-ऋणात्मक मानों से संबंधित सभी अभ्यावेदन intपहले से ही एक ही मान द्वारा उठाए गए हैं uint, इसलिए नकारात्मक मूल्यों के अनुरूप सभी अभ्यावेदन इससे अधिक के कुछ मूल्य के intअनुरूप हैं , लेकिन यह वास्तव में मायने नहीं रखता है कि कौन सा मूल्य है। (वास्तव में, सभी कि वास्तव में आवश्यक है कि शून्य दोनों में एक ही तरह से प्रस्तुत किया जाता है है और ।)uint0x7FFFFFFFintuint

3
@hvd: समझाने के लिए धन्यवाद। आप सही हैं, यह दो पूरक नहीं है जो मायने रखता है; यह वह आवश्यकता है जिसका आपने उल्लेख किया है और यह तथ्य कि अंतर्निहित बिट पैटर्न को बदलने के बिना cgt.unएक के intरूप में व्यवहार करता है uint। (कल्पना कीजिए कि cgt.unपहले उस मामले आपको स्पष्ट रूप से स्थानापन्न नहीं कर सकता है में 0 पर सभी ऋणात्मक संख्याओं मैप करके ठीक underflows करने की कोशिश करेंगे > 0के लिए != 0।)
stakx - अब योगदान

2
मुझे यह आश्चर्य की बात है कि एक वस्तु के संदर्भ की तुलना किसी दूसरे के उपयोग >से सत्यापित करने योग्य आईएल है। इस तरह एक दो गैर-अशक्त वस्तुओं की तुलना कर सकता है और एक बूलियन परिणाम (जो गैर-नियतात्मक है) प्राप्त कर सकता है। यह एक स्मृति-सुरक्षा मुद्दा नहीं है, लेकिन यह अशुद्ध डिजाइन की तरह महसूस करता है जो सुरक्षित प्रबंधित कोड की सामान्य भावना में नहीं है। यह डिज़ाइन इस तथ्य को लीक करता है कि ऑब्जेक्ट संदर्भ को पॉइंटर्स के रूप में लागू किया जाता है। .NET CLI के डिज़ाइन दोष की तरह लगता है।
यूआर

3
@usr: बिल्कुल! सीएलआई मानक के खंड III.1.1.4 में कहा गया है कि "वस्तु संदर्भ (प्रकार ओ) पूरी तरह से अपारदर्शी हैं" और "केवल तुलनात्मक संचालन की अनुमति समानता और असमानता है ..."। शायद क्योंकि वस्तु संदर्भ कर रहे हैं नहीं स्मृति पतों के संदर्भ में परिभाषित, मानक भी धारणात्मक (जैसे देखने की परिभाषा 0 से अलग अशक्त संदर्भ रखने के लिए ख्याल रखता है ldnull, initobjऔर newobj)। तो cgt.unअशक्त संदर्भ के खिलाफ वस्तु संदर्भों की तुलना करने का उपयोग एक से अधिक तरीकों से खंड III.1.1.4 के विपरीत प्रतीत होता है।
stakx - अब
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.