यह अनंत लूप में क्यों जाता है?


493

मेरे पास निम्नलिखित कोड हैं:

public class Tests {
    public static void main(String[] args) throws Exception {
        int x = 0;
        while(x<3) {
            x = x++;
            System.out.println(x);
        }
    }
}

हम जानते हैं कि उसे सिर्फ x++या तो लेखनी करनी चाहिए थी x=x+1, लेकिन उस पर x = x++पहले xखुद ही ध्यान देना चाहिए , और बाद में उसे बढ़ाना चाहिए। मूल्य के रूप में क्यों xजारी है 0?

--अपडेट करें

यहां देखें:

public class Tests extends java.lang.Object{
public Tests();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[])   throws java.lang.Exception;
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   iconst_3
   4:   if_icmpge   22
   7:   iload_1
   8:   iinc    1, 1
   11:  istore_1
   12:  getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   15:  iload_1
   16:  invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   19:  goto    2
   22:  return

}

मैं समझने की कोशिश करने के निर्देशों के बारे में पढ़ूंगा ...


8
मुझे संदेह है कि क्या हो रहा है: 1. एक रजिस्टर में लोड x (= 0); 2. वेतन वृद्धि x (x = 1); 3. रजिस्टर मान को x (x = 0) में सहेजें। C / C ++ में यह अपरिभाषित व्यवहार होगा क्योंकि 2 और 3 के क्रम को परिभाषित करने के लिए कोई औपचारिक अनुक्रम बिंदु नहीं है। उम्मीद है कि कोई व्यक्ति आपको जावा कल्पना के समकक्ष कुछ उद्धृत कर सकता है।
रुप

19
हमने C ++ में यह देखने की कोशिश की कि क्या होगा, और यह 1,2,3 प्रिंट करता है और बाहर निकलता है। मैंने इसकी आशा नहीं की थी। मुझे लगता है कि यह संकलक निर्भर है, क्योंकि यह अपरिभाषित व्यवहार है। हमने gnu g ++ का उपयोग किया।
शोक

13
@saj x++पोस्ट-इन्क्रीमेंट है; परिणामx= का असाइनमेंट है ; परिणाम की मूल है (और वहाँ वेतन वृद्धि का एक पक्ष प्रभाव है, लेकिन यह परिणाम नहीं बदलता है), तो यह समझा जा सकता हैx++xvar tmp = x; x++; x = tmp;
मार्क Gravell

5
अब जब मेरे पास एक लोकप्रिय प्रश्न है, मुझे खेद है, (दोहराया गया) उत्तर सही आने के बाद भी नहीं रुके हैं। मेरी "हाल की गतिविधि" स्क्रीन एक ही उत्तर से भरी हुई है, और अधिक आ रही है ...
टॉम ब्रिटो

3
@ रोब वर्म्यूलेन आप टिप्पणी करने से पहले पूरा प्रश्न पढ़ना चाह सकते हैं ..;) यह मेरा एक छात्र द्वारा बनाया गया एक कोड था, और मैं इस व्यवहार के कारण उत्सुक था।
टॉम ब्रिटो

जवाबों:


354

नोट : मूल रूप से मैंने चित्रण के प्रयोजनों के लिए इस उत्तर में C # कोड पोस्ट किया, क्योंकि C # आपको कीवर्ड के intसंदर्भ में पैरामीटर पारित करने की अनुमति देता है ref। मैंने MutableIntGoogle पर पाए जाने वाले प्रथम श्रेणी का उपयोग करके वास्तविक कानूनी जावा कोड के साथ इसे अपडेट करने का निर्णय लिया हैref C # में कुछ किया है उसे । मैं वास्तव में नहीं बता सकता कि अगर मदद करता है या जवाब दर्द होता है। मैं कहूंगा कि मैंने व्यक्तिगत रूप से इतना जावा विकास नहीं किया है; इसलिए मुझे पता है कि इस बिंदु को स्पष्ट करने के लिए बहुत अधिक मुहावरेदार तरीके हो सकते हैं।


शायद अगर हम एक विधि लिखते हैं जो x++यह करता है के बराबर करने के लिए यह स्पष्ट कर देगा।

public MutableInt postIncrement(MutableInt x) {
    int valueBeforeIncrement = x.intValue();
    x.add(1);
    return new MutableInt(valueBeforeIncrement);
}

सही? उत्तीर्ण मूल्य में वृद्धि और मूल मूल्य लौटाएँ: यह पोस्टाइन्क्रिमेंट ऑपरेटर की परिभाषा है।

अब, देखते हैं कि यह व्यवहार आपके उदाहरण कोड में कैसे चलता है:

MutableInt x = new MutableInt();
x = postIncrement(x);

postIncrement(x)क्या करता है? वृद्धि x, हाँ। और फिर जो वेतन वृद्धि से पहले था वह लौटाता हैx । यह रिटर्न वैल्यू तब सौंपी जाती है x

अतः दिए गए मानों का क्रम x0 है, फिर 1, फिर 0 है।

यदि हम उपरोक्त को फिर से लिखते हैं तो यह और भी स्पष्ट हो सकता है:

MutableInt x = new MutableInt();    // x is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
x = temp;                           // Now x is 0 again.

इस तथ्य पर आपका निर्धारण कि जब आप xउपरोक्त असाइनमेंट के बाईं ओर से प्रतिस्थापित करते हैं y, "तो आप देख सकते हैं कि यह पहले एक्स को बढ़ाता है, और बाद में इसे y करने के लिए" मुझे हड़ताली के रूप में भ्रमित करता है। ऐसा नहीं है xकि इसे सौंपा जा रहा है y; यह पूर्व में दिया गया मान हैx । वास्तव में, इंजेक्शन लगाने yसे ऊपर के परिदृश्य से कुछ अलग नहीं होता है; हमें बस मिल गया है:

MutableInt x = new MutableInt();    // x is 0.
MutableInt y = new MutableInt();    // y is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
y = temp;                           // y is still 0.

तो यह स्पष्ट है: x = x++प्रभावी रूप से एक्स के मूल्य को नहीं बदलता है। यह हमेशा x को मान x 0 , फिर x 0 + 1 और फिर x 0 को फिर से मानने का कारण बनता है ।


अद्यतन : संयोग से, ऐसा न हो कि आपको संदेह हो कि xकभी "इनक्रीमेंट ऑपरेशन" और असाइनमेंट ऊपर दिए गए उदाहरण के बीच "1" को सौंपा गया है, मैंने एक साथ एक त्वरित डेमो फेंक दिया है कि यह व्याख्या करने के लिए कि यह मध्यवर्ती मान वास्तव में "अस्तित्व में है," हालांकि यह है निष्पादित धागे पर "देखा" नहीं जाना चाहिए।

डेमो x = x++;एक लूप में कॉल करता है जबकि एक अलग थ्रेड लगातार xकंसोल के मूल्य को प्रिंट करता है।

public class Main {
    public static volatile int x = 0;

    public static void main(String[] args) {
        LoopingThread t = new LoopingThread();
        System.out.println("Starting background thread...");
        t.start();

        while (true) {
            x = x++;
        }
    }
}

class LoopingThread extends Thread {
    public @Override void run() {
        while (true) {
            System.out.println(Main.x);
        }
    }
}

नीचे उपरोक्त प्रोग्राम के आउटपुट का एक अंश है। 1s और 0s दोनों की अनियमित घटना पर ध्यान दें।

बैकग्राउंड थ्रेड शुरू ...
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
0
1

1
आपको जावा में संदर्भ द्वारा पास करने के लिए एक वर्ग बनाने की आवश्यकता नहीं है (हालांकि यह निश्चित रूप से काम करेगा)। आप Integerकक्षा का उपयोग कर सकते हैं , जो मानक पुस्तकालय का हिस्सा है, और यहां तक ​​कि int लगभग पारदर्शी तरीके से ऑटो-बॉक्स होने का लाभ भी है ।
rmeador

3
@rmeador Integer अपरिवर्तनीय है, इसलिए आप अभी भी इसका मूल्य नहीं बदल सकते। एटोमिकइन्जर, हालांकि, उत्परिवर्तनीय है।
ILMTitan

5
@ दान: वैसे, xआपके अंतिम उदाहरण में घोषित किया जाना चाहिए volatile, अन्यथा यह एक अपरिभाषित व्यवहार है और 1एस को लागू करना विशिष्ट है।
axtavt

4
@burkestar: मुझे नहीं लगता कि इस मामले में लिंक काफी उपयुक्त है, क्योंकि यह एक जावा प्रश्न है और (जब तक कि मुझसे कोई गलती नहीं है) व्यवहार वास्तव में C ++ में अपरिभाषित है।
दान ताओ

5
@ टॉम ब्रिटो - सी में यह परिभाषित नहीं है ... असाइनमेंट से पहले या बाद में किया ++ जा सकता है। व्यावहारिक रूप से, एक कंपाइलर हो सकता है जो जावा के समान काम करता है, लेकिन आप इस पर शर्त नहीं लगाना चाहेंगे।
detly

170

x = x++ निम्नलिखित तरीके से काम करता है:

  • पहले यह अभिव्यक्ति का मूल्यांकन करता है x++। इस अभिव्यक्ति का मूल्यांकन एक अभिव्यक्ति मूल्य (जो xवेतन वृद्धि से पहले का मूल्य है ) और वेतन वृद्धि पैदा करता है x
  • बाद में यह xबढ़े हुए मूल्य को अधिलेखित करने के लिए अभिव्यक्ति मूल्य प्रदान करता है ।

इसलिए, घटनाओं का क्रम इस प्रकार दिखता है (यह एक वास्तविक विघटित बायोटेक है, जैसा कि javap -cमेरी टिप्पणियों के साथ निर्मित होता है ):

   8: iload_1 // स्टैक में x का वर्तमान मान याद रखें
   9: iinc 1, 1 // इंक्रीमेंट x (स्टैक नहीं बदलता है)
   12: istore_1 // स्टैक से x तक रीमेबर्ड मान लिखें

तुलना के लिए x = ++x:

   8: iinc 1, 1 // इंक्रीमेंट x
   11: iload_1 // स्टैक पर x का पुश मान
   12: istore_1 // स्टैक से x तक पॉप वैल्यू

यदि आप एक परीक्षण करते हैं, तो आप देख सकते हैं कि यह पहले वेतन वृद्धि, और बाद में विशेषता है। तो यह शून्य विशेषता नहीं होना चाहिए।
टॉम ब्रिटो

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

अपने बाइटकोड आउटपुट के बारे में: ध्यान दें कि iincएक चर बढ़ाता है, यह एक स्टैक मूल्य नहीं बढ़ाता है, और न ही यह स्टैक पर एक मूल्य छोड़ता है (लगभग हर दूसरे अंकगणितीय ऑप के विपरीत)। आप ++xतुलना के लिए उत्पन्न कोड जोड़ना चाह सकते हैं ।
एनॉन

3
@ रिप इसे C या C ++ में परिभाषित नहीं किया जा सकता है, लेकिन जावा में, इसे अच्छी तरह से परिभाषित किया गया है।
ILMTitan 14

1
दिलचस्प लेख Angelikalanger.com/Articles/VSJ/SequencePoints/…
Jaydee

104

ऐसा इसलिए होता है क्योंकि मूल्य में xवृद्धि नहीं होती है।

x = x++;

के बराबर है

int temp = x;
x++;
x = temp;

स्पष्टीकरण:

आइए इस ऑपरेशन के लिए बाइट कोड देखें। एक नमूना वर्ग पर विचार करें:

class test {
    public static void main(String[] args) {
        int i=0;
        i=i++;
    }
}

अब इस पर क्लास डिस्सेम्बलर चल रहा है:

$ javap -c test
Compiled from "test.java"
class test extends java.lang.Object{
test();
  Code:
   0:    aload_0
   1:    invokespecial    #1; //Method java/lang/Object."<init>":()V
   4:    return

public static void main(java.lang.String[]);
  Code:
   0:    iconst_0
   1:    istore_1
   2:    iload_1
   3:    iinc    1, 1
   6:    istore_1
   7:    return
}

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

हमें पर नजर डालते हैं स्मृति सहायकों में main()विधि:

  • iconst_0: स्थिर मान 0 स्टैक पर धकेल दिया जाता है।
  • istore_1: स्टैक के शीर्ष तत्व को पॉपअप किया जाता है और इंडेक्स के साथ स्थानीय चर में संग्रहीत किया जाता 1
    है x
  • iload_1: उस स्थान पर 1मान, x जिसका मूल्य है 0, स्टैक में धकेल दिया जाता है।
  • iinc 1, 1: स्मृति स्थान पर मान से 1बढ़ा हुआ है 1। तो xअब बन जाता है 1
  • istore_1: स्टैक के शीर्ष पर मान मेमोरी स्थान पर संग्रहीत होता है 1। यह इसके बढ़े हुए मूल्य 0को x अधिलेखित करने के लिए सौंपा गया है ।

इसलिए xअनंत लूप के परिणामस्वरूप मूल्य में परिवर्तन नहीं होता है।


5
वास्तव में यह बढ़ जाता है (यानी का अर्थ ++), लेकिन चर बाद में अधिलेखित हो जाता है।
प्राग्मैन

10
int temp = x; x = x + 1; x = temp;अपने उदाहरण में एक tautology का उपयोग नहीं करना बेहतर है।
स्कॉट चैंबरलेन 14

52
  1. उपसर्ग संकेतन चर का मूल्यांकन करेगा पहले अभिव्यक्ति का मूल्यांकन किया जाता है।
  2. उपसर्ग संकेतन अभिव्यक्ति के मूल्यांकन के बाद वृद्धि होगी।

हालांकि " =" में " " की तुलना में कम ऑपरेटर पूर्वता है ++

इसलिए x=x++;निम्नानुसार मूल्यांकन करना चाहिए

  1. x असाइनमेंट के लिए तैयार (मूल्यांकन)
  2. x वृद्धि
  3. का पिछला मूल्य xकरने के लिए सौंपा x

यह सबसे अच्छा जवाब है। कुछ मार्कअप ने इसे थोड़ा और खड़ा करने में मदद की होगी।
जस्टिन फोर्स

1
ये गलत है। यह पूर्वता के बारे में नहीं है। सी और सी ++ की ++तुलना =में अधिक पूर्वता है , लेकिन उन भाषाओं में बयान अपरिभाषित है।
मैथ्यू फ्लेशेन

2
मूल प्रश्न जावा
जेडी

34

कोई भी जवाब नहीं है जहाँ पर काफी जगह है, तो यहाँ जाता है:

जब आप लिख रहे हैं int x = x++, तो आप xनए मूल्य पर स्वयं होने का असाइन नहीं कर रहे हैं , आप अभिव्यक्ति xका रिटर्न मान होने का असाइन कर रहे हैं x++कॉलिन कोचरन के उत्तरx में संकेत के अनुसार मूल मूल्य ऐसा होता है ।

मज़े के लिए, निम्नलिखित कोड का परीक्षण करें:

public class Autoincrement {
        public static void main(String[] args) {
                int x = 0;
                System.out.println(x++);
                System.out.println(x);
        }
}

परिणाम होगा

0
1

अभिव्यक्ति का वापसी मूल्य प्रारंभिक मूल्य है x, जो शून्य है। लेकिन बाद में, के मूल्य को पढ़ते समय x, हम अद्यतन मूल्य प्राप्त करते हैं, वह एक है।


मैं बाइटकोड लाइनों को समझने की कोशिश करूंगा, मेरे अपडेट को देखूंगा, तो यह स्पष्ट हो जाएगा .. :)
टॉम ब्रिटो

यह समझने में मेरे लिए Println () का उपयोग करना बहुत मददगार था।
ErikE

29

यह पहले से ही अन्य द्वारा अच्छी तरह से समझाया गया है। मैं सिर्फ प्रासंगिक जावा विनिर्देश अनुभागों के लिंक शामिल करता हूं।

x = x ++ एक अभिव्यक्ति है। जावा मूल्यांकन क्रम का पालन करेगा । यह पहले एक्स एक्स एक्स का मूल्यांकन करेगा, जो एक्स को बढ़ाएगा और एक्स के पिछले मूल्य पर परिणाम मूल्य निर्धारित करेगा । तब यह चर x को अभिव्यक्ति परिणाम देगा । अंत में, x अपने पिछले मूल्य पर वापस आ गया है।


1
+1। यह वास्तविक प्रश्न का सबसे अच्छा उत्तर है, "क्यों?"
मैथ्यू फ्लेशेन

18

यह कथन:

x = x++;

इस तरह मूल्यांकन करता है:

  1. xढेर पर धक्का ;
  2. वृद्धि x;
  3. xस्टैक से पॉप ।

तो मान अपरिवर्तित है। इसकी तुलना करें:

x = ++x;

जो मूल्यांकन करता है:

  1. वृद्धि x;
  2. xढेर पर धक्का ;
  3. xस्टैक से पॉप ।

आप क्या चाहते हैं:

while (x < 3) {
  x++;
  System.out.println(x);
}

13
निश्चित रूप से सही कार्यान्वयन, लेकिन सवाल 'क्यों?' है।
पी.कैम्पबेल

1
मूल कोड एक्स पर पोस्ट-इन्क्रीमेंट का उपयोग कर रहा था और फिर इसे एक्स को असाइन कर रहा था। x वेतन वृद्धि से पहले x के लिए बाध्य होगा, इसलिए यह मूल्यों को कभी नहीं बदलेगा।
विकिपीडिया

5
@ क्लेटस मैं नीच नहीं हूं, लेकिन आपके प्रारंभिक उत्तर में स्पष्टीकरण नहीं था। यह सिर्फ कहा 'x ++ `करते हैं।
पेटार मिनचेव

4
@ क्लेटस: मैंने नीचा नहीं देखा, लेकिन मूल रूप से आपका जवाब सिर्फ x++कोड स्निपेट था।
पी.कैम्पबेल

10
स्पष्टीकरण भी गलत है। यदि कोड पहले x को x को निर्दिष्ट करता है और फिर x को बढ़ाता है, तो यह ठीक काम करेगा। बस x++;अपने समाधान में परिवर्तन करें x=x; x++;और आप वही कर रहे हैं जो आप दावा करते हैं कि मूल कोड क्या कर रहा है।
Wooble

10

जवाब बहुत सीधा है। यह आदेश के साथ क्या करना है चीजों का मूल्यांकन किया जाता है। x++मान xबढ़ाता है, फिर बढ़ाता हैx

नतीजतन, अभिव्यक्ति x++का मूल्य है 0। इसलिए आप x=0हर बार लूप में काम कर रहे हैं । निश्चित रूप से x++इस मूल्य में वृद्धि होती है, लेकिन असाइनमेंट से पहले ऐसा होता है।


1
वाह, इस पृष्ठ पर बहुत विस्तार है जब उत्तर छोटा और सरल है अर्थात यह एक है।
चार्ल्स गुडविन

8

से http://download.oracle.com/javase/tutorial/java/nutsandbolts/op1.html

वृद्धिशील / वेतन वृद्धि ऑपरेटरों को (उपसर्ग) से पहले या बाद (पोस्टफिक्स) ऑपरेंड में लागू किया जा सकता है। कोड परिणाम ++; और ++ परिणाम; दोनों एक के बाद बढ़े हुए परिणाम में समाप्त हो जाएंगे। अंतर केवल इतना है कि उपसर्ग संस्करण (++ परिणाम) बढ़े हुए मूल्य का मूल्यांकन करता है, जबकि पोस्टफ़िक्स संस्करण (परिणाम ++) मूल मूल्य का मूल्यांकन करता है । यदि आप केवल एक साधारण वृद्धि / वेतन वृद्धि कर रहे हैं, तो यह वास्तव में कोई फर्क नहीं पड़ता कि आप किस संस्करण को चुनते हैं। लेकिन अगर आप इस ऑपरेटर को एक बड़ी अभिव्यक्ति के हिस्से में उपयोग करते हैं, तो आपके द्वारा चुना गया एक महत्वपूर्ण अंतर हो सकता है।

समझाने के लिए, निम्नलिखित प्रयास करें:

    int x = 0;
    int y = 0;
    y = x++;
    System.out.println(x);
    System.out.println(y);

जो 1 और 0 प्रिंट करेगा।


1
यह मूल्यांकन परिणाम नहीं है कि यह मुद्दा है, हालांकि, यह दुकानों का क्रम है।
रुप

2
मैं असहमत हूं। यदि x = 0 तो x ++ वापस आ जाएगा। इसलिए x = x ++ में x = 0. परिणाम होगा
कॉलिन कोचरन

इस बारे में रूप सही है। यह उन दुकानों का क्रम है जो इस विशेष मामले में जारी है। y = x ++ x = x ++ के समान नहीं है; बाद वाले पर एक्स को एक ही अभिव्यक्ति में 2 मान दिए जा रहे हैं। बाएं हाथ x को एक्सप्रेशन x ++ के मूल्यांकन का परिणाम सौंपा जा रहा है , जो कि 0. राइट हैंड x है, जिसे 1. में बढ़ाया जा रहा है। जिस क्रम में ये 2 असाइनमेंट होते हैं, वह समस्या क्या है। पिछली पोस्टों से यह स्पष्ट है कि यह काम करने का तरीका है: eval = x ++ => eval == 0: increment right x => x == 1: left x = eval => x == 0
Michael Ekoka

7

आप निम्नलिखित व्यवहार को प्रभावी ढंग से प्राप्त कर रहे हैं।

  1. सही पक्ष के "परिणाम" के रूप में x (जो कि 0 है) के मान को पकड़ो
  2. वेतन वृद्धि x (तो x अब 1 है)
  3. सही पक्ष के परिणाम को असाइन करें (जो 0 के रूप में सहेजा गया था) को x (x अब 0 है)

यह विचार किया जा रहा है कि पोस्ट-इन्क्रीमेंट ऑपरेटर (x ++) उस चर को बढ़ाता है, जिसमें प्रश्न है कि जिस समीकरण में इसका उपयोग किया गया है, उसके उपयोग के लिए उसका मान लौटाएं।

संपादित करें: टिप्पणी के कारण थोड़ा सा जोड़ना। इसे निम्न की तरह समझें।

x = 1;        // x == 1
x = x++ * 5;
              // First, the right hand side of the equation is evaluated.
  ==>  x = 1 * 5;    
              // x == 2 at this point, as it "gave" the equation its value of 1
              // and then gets incremented by 1 to 2.
  ==>  x = 5;
              // And then that RightHandSide value is assigned to 
              // the LeftHandSide variable, leaving x with the value of 5.

ठीक है, लेकिन चरण 2 और 3 का क्रम क्या है?
रुप

@Rup - भाषा इसे परिभाषित करती है। समीकरण के दाईं ओर पहले मूल्यांकन किया जाता है (इस मामले में, "x ++"), और परिणाम बाईं ओर चर को सौंपा गया है। कि भाषा कैसे काम करती है। जहाँ तक समीकरण के लिए "x ++" "रिटर्निंग" x है, तो यह है कि पोस्टफ़िक्स इंक्रीमेंट ऑपरेटर कैसे काम करता है (x का मान लौटाएँ, फिर इसे बढ़ाएँ)। यदि यह "--x" होता, तो यह होता (वेतन वृद्धि x, फिर मान लौटाता)। रिटर्न वहाँ सही शब्द नहीं है, लेकिन आपको यह विचार मिलता है।
RHSeeger

7

आपको वास्तव में समझने के लिए मशीन कोड की आवश्यकता नहीं है।

परिभाषाओं के अनुसार:

  1. असाइनमेंट ऑपरेटर दाईं ओर की अभिव्यक्ति का मूल्यांकन करता है, और इसे एक अस्थायी चर में संग्रहीत करता है।

    1.1। X का वर्तमान मान इस अस्थायी चर में कॉपी किया गया है

    1.2। x अब बढ़ा हुआ है।

  2. अस्थायी चर को फिर बायीं ओर अभिव्यक्ति में कॉपी किया जाता है, जो संयोग से x है! तो इसीलिए x का पुराना मान फिर से स्वयं में कॉपी किया गया है।

यह बहुत सरल है।


5

ऐसा इसलिए है क्योंकि यह इस मामले में कभी नहीं बढ़ जाता है। x++इस तरह से इस मामले पर वेतन वृद्धि से पहले पहले इसके मूल्य का उपयोग करेंगे:

x = 0;

लेकिन अगर आप ऐसा ++x;करेंगे तो वृद्धि होगी।


यदि आप एक परीक्षण करते हैं, तो आप देख सकते हैं कि यह पहले वेतन वृद्धि, और बाद में विशेषता है। तो यह शून्य विशेषता नहीं होना चाहिए।
टॉम ब्रिटो

1
@Tom: मेरा उत्तर देखें - मैं एक परीक्षण में दिखाता हूं कि x ++ वास्तव में x का पुराना मान लौटाता है। बस यहीं से टूट जाता है।
रॉबर्ट मुंटेनु

"यदि आप एक परीक्षण करते हैं" - कुछ लोगों को लगता है कि सी में लिखा गया एक परीक्षण हमें बताता है कि जावा क्या करेगा, जब यह हमें यह भी नहीं बताएगा कि सी क्या करेगा।
जिम बाल्टर

3

मान 0 पर रहता है क्योंकि मान 0 x++है। इस स्थिति में यह मायने नहीं रखता है कि मान xबढ़ाया गया है या नहीं, असाइनमेंट x=0निष्पादित किया गया है। यह x("बहुत कम समय" के लिए 1) के अस्थायी वेतन वृद्धि को अधिलेखित कर देगा ।


लेकिन x ++ एक पोस्ट ऑपरेशन है। अतः असाइनमेंट पूरा होने के बाद x को बढ़ाना होगा।
सागर वी।

1
@ सागर वी: केवल अभिव्यक्ति के x++लिए, पूरे असाइनमेंट के लिए नहींx=x++;
प्रागमन

नहीं, मुझे लगता है कि असाइनमेंट में उपयोग किए जाने वाले x के मान के बाद ही इसे बढ़ाना होगा।
रुप

1

यह काम करता है कि आप दूसरे से कैसे उम्मीद करते हैं। यह उपसर्ग और उपसर्ग के बीच का अंतर है।

int x = 0; 
while (x < 3)    x = (++x);

1

एक फ़ंक्शन कॉल के रूप में x ++ के बारे में सोचें जो एक्स पहले था "रिटर्न" वेतन वृद्धि (यही कारण है कि इसे पोस्ट-इन्क्रीमेंट कहा जाता है)।

इसलिए ऑपरेशन का क्रम है:
1: वेतन वृद्धि x से पहले x का मान
2: वेतन वृद्धि x
3: कैश्ड मान लौटाएं (x इससे पहले कि यह बढ़ाया गया था)
4: वापसी मान x को सौंपा गया है


ठीक है, लेकिन चरण 3 और 4 का क्रम क्या है?
रुप

"रिटर्न क्या एक्स था वेतन वृद्धि से पहले" गलत है, मेरा अपडेट देखें
टॉम ब्रिटो

हकीकत में चरण 3 और 4 अलग-अलग ऑपरेशन नहीं हैं - यह वास्तव में एक फ़ंक्शन कॉल नहीं है जो एक मूल्य लौटाता है, यह सिर्फ उस तरह से सोचने में मदद करता है। जब भी आपके पास एक असाइनमेंट होता है तो दाहिने हाथ की तरफ "मूल्यांकन" किया जाता है, फिर परिणाम बाएं हाथ की ओर सौंपा जाता है, मूल्यांकन परिणाम को वापसी मूल्य के रूप में माना जा सकता है क्योंकि यह आपको संचालन के आदेश को समझने में मदद करता है, लेकिन यह वास्तव में नहीं है ।
jhabbott

उफ़, सच। मेरा मतलब चरण 2 और 4 है - लौटाया गया मूल्य बढ़े हुए मूल्य के शीर्ष पर क्यों जमा होता है?
रुप

1
यह एक असाइनमेंट ऑपरेशन की परिभाषा का हिस्सा है, पहले दाहिने हाथ की तरफ पूरी तरह से मूल्यांकन किया जाता है, फिर परिणाम बाएं हाथ की तरफ सौंपा जाता है।
jhabbott

1

जब ++ आरएचएस पर होता है, तो संख्या बढ़ने के पहले परिणाम वापस कर दिया जाता है। ++ x में बदलें और यह ठीक रहा होगा। जावा ने वेतन वृद्धि के बजाय एकल ऑपरेशन (x से x का असाइनमेंट) करने के लिए इसे अनुकूलित किया होगा।


1

खैर जहां तक ​​मैं देख सकता हूं, त्रुटि तब होती है, क्योंकि असाइनमेंट बढ़े हुए मूल्य को ओवरराइड करने के कारण, वेतन वृद्धि से पहले के मूल्य के साथ, अर्थात यह वेतन वृद्धि को पूर्ववत करता है।

विशेष रूप से, "x ++" अभिव्यक्ति में, '' x '' का मान है जो वेतन वृद्धि से पहले "++ x" के विपरीत है, जिसमें वृद्धि के बाद 'x' का मान है।

यदि आप बाइटकोड की जांच करने में रुचि रखते हैं, तो हम प्रश्न में तीन पंक्तियों पर एक नज़र डालेंगे:

 7:   iload_1
 8:   iinc    1, 1
11:  istore_1

7: iload_1 # स्टैक पर 2 के स्थानीय वेरिएबल का मान
8 पर रखेगा: iinc 1,1 # 1 के साथ 2 वें स्थानीय वेरिएबल को बढ़ाएगा, ध्यान दें कि यह स्टैक को अछूता छोड़ देता है!
9: istore_1 # ढेर के शीर्ष को पॉप करेगा और इस तत्व के मूल्य को दूसरे स्थानीय चर में बचाएगा
(आप प्रत्येक JVM निर्देश के प्रभावों को यहां पढ़ सकते हैं )

यही कारण है कि उपरोक्त कोड अनिश्चित काल तक लूप होगा, जबकि ++ x वाला संस्करण नहीं होगा। ++ x के लिए बायटेकोड काफी अलग दिखना चाहिए, जहां तक ​​मुझे याद है कि 1.3 जावा कंपाइलर जो मैंने एक साल पहले लिखा था, बाईटेकोड को कुछ इस तरह से जाना चाहिए:

iinc 1,1
iload_1
istore_1

तो बस दो पहली पंक्तियों की अदला-बदली करें, शब्दार्थ को बदल दें ताकि वेतन वृद्धि के बाद स्टैक के शीर्ष पर छोड़ दिया गया मूल्य (यानी 'अभिव्यक्ति' का मूल्य) वेतन वृद्धि के बाद का मूल्य हो।


1
    x++
=: (x = x + 1) - 1

इसलिए:

   x = x++;
=> x = ((x = x + 1) - 1)
=> x = ((x + 1) - 1)
=> x = x; // Doesn't modify x!

जहाँ तक

   ++x
=: x = x + 1

इसलिए:

   x = ++x;
=> x = (x = x + 1)
=> x = x + 1; // Increments x

बेशक अंतिम परिणाम केवल x++;या ++x;एक लाइन पर ही के रूप में ही है।



0

मुझे आश्चर्य है कि अगर जावा कल्पना में कुछ भी है जो इस के व्यवहार को ठीक से परिभाषित करता है। (स्पष्ट रूप से उस कथन का निहितार्थ यह है कि मैं जांच के लिए बहुत आलसी हूं।)

टॉम के बायटेकोड से ध्यान दें, प्रमुख रेखाएं 7, 8 और 11. पंक्ति 7 लोड एक्स हैं जो गणना स्टैक में हैं। पंक्ति 8 वेतन वृद्धि x। पंक्ति 11 स्टैक से x पर मान को संग्रहीत करती है। सामान्य मामलों में जहां आप स्वयं को मान नहीं दे रहे हैं, मुझे नहीं लगता कि कोई कारण होगा कि आप लोड नहीं कर सकते, स्टोर कर सकते हैं, फिर वेतन वृद्धि। आपको वही परिणाम मिलेगा।

जैसे, मान लीजिए कि आपके पास एक अधिक सामान्य मामला था जहां आपने कुछ लिखा था जैसे: z = (x ++) + (y ++);

चाहे उसने कहा हो (तकनीकी को छोड़ने के लिए स्यूडोकोड)

load x
increment x
add y
increment y
store x+y to z

या

load x
add y
store x+y to z
increment x
increment y

अप्रासंगिक होना चाहिए। या तो कार्यान्वयन वैध होना चाहिए, मुझे लगता है।

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


0

मुझे लगता है क्योंकि जावा ++ में = (असाइनमेंट) की तुलना में अधिक पूर्वता है ... क्या यह है? को देखो http://www.cs.uwf.edu/~eelsheik/cop2253/resources/op_precedence.html ...

उसी तरह यदि आप x = x + 1 लिखते हैं ... + में = (असाइनमेंट) की तुलना में अधिक पूर्वता है


यह मिसाल का सवाल नहीं है। सी और सी ++ की ++तुलना =में उच्च वरीयता है , लेकिन बयान अपरिभाषित है।
मैथ्यू फ्लैशेन

0

x++अभिव्यक्ति का मूल्यांकन x++भाग के बाद मूल्य को प्रभावित मूल्यांकन के बाद नहीं, बयान । इसलिए x = x++प्रभावी ढंग से इसमें अनुवाद किया गया है

int y = x; // evaluation
x = x + 1; // increment part
x = y; // assignment

0

मान को एक से बढ़ाने से पहले, मान चर को सौंपा गया है।


0

यह इसलिए हो रहा है क्योंकि यह पोस्ट इंक्रीमेंटेड है। इसका अर्थ है कि अभिव्यक्ति के मूल्यांकन के बाद चर बढ़ जाता है।

int x = 9;
int y = x++;

x अब 10 है, लेकिन y 9 है, x का मान बढ़ने से पहले।

पोस्ट इंक्रीमेंट की परिभाषा में और देखें ।


1
आपका x/ yउदाहरण वास्तविक कोड से अलग है, और अंतर प्रासंगिक है। आपके लिंक में जावा का उल्लेख भी नहीं है। जिन दो भाषाओं का यह उल्लेख करता है , उनके लिए प्रश्न में कथन अपरिभाषित है।
मैथ्यू फ्लेशेन

0

नीचे दिए गए कोड की जाँच करें,

    int x=0;
    int temp=x++;
    System.out.println("temp = "+temp);
    x = temp;
    System.out.println("x = "+x);

आउटपुट होगा,

temp = 0
x = 0

post incrementका मतलब है कि वेतन वृद्धि और वेतन वृद्धि से पहले मूल्य वापस करें । इसीलिए मूल्य tempहै 0। तो क्या हुआ temp = iऔर यह एक लूप में है (कोड की पहली पंक्ति को छोड़कर)। जैसे सवाल में ही !!!!


-1

वेतन वृद्धि ऑपरेटर को उसी चर पर लागू किया जाता है, जिस पर आप काम कर रहे हैं। वह मुसीबत पूछ रहा है। मुझे यकीन है कि आप इस कार्यक्रम को चलाते समय अपने x चर का मूल्य देख सकते हैं .... यही स्पष्ट करना चाहिए कि लूप कभी समाप्त क्यों नहीं होता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.