I ++ और ++ i में क्या अंतर है?


204

मैं उन्हें देखा है दोनों सी # कोड के कई टुकड़ों में इस्तेमाल किया जा रहा है, और मैं जब उपयोग करने के लिए जानना चाहते हैं i++या ++i( iजैसे एक नंबर चर जा रहा है int, float, double, आदि)। जो कोई भी यह जानता है?


13
सिवाय इसके कि जहां इससे कोई फर्क नहीं पड़ता है, आपको कभी भी एक का उपयोग नहीं करना चाहिए, क्योंकि यह सिर्फ लोगों से वही सवाल पूछने जा रहा है जो आप अभी पूछ रहे हैं। "मुझे मत सोचो" कोड के साथ-साथ डिजाइन पर भी लागू होता है।
इंस्टेंट हंटर


2
@ डॅलर: क्या आपने मेरी टिप्पणी के लिंक भी पढ़े हैं? पहला सी # के बारे में है, और दूसरा एक स्वीकृत जवाब के साथ भाषा-अज्ञेय है जो सी # पर केंद्रित है।
गुनोविस

7
@gnovice, पहला प्रदर्शन अंतर के बारे में पूछता है जबकि मैंने वास्तविक कोड-वार अंतर के बारे में पूछा था, दूसरा लूप में अंतर के बारे में है जबकि मैंने सामान्य रूप से अंतर पूछा है, और तीसरा C ++ के बारे में है।
डैलोर

1
मेरा मानना ​​है कि यह लूप में i ++ और ++ i के बीच अंतर का डुप्लिकेट नहीं है ? - जैसा कि डोलर अपनी टिप्पणी में कहते हैं , दूसरे प्रश्न के ऊपर विशेष रूप से एक लूप के भीतर उपयोग के बारे में पूछता है।
च्यू एक्स

जवाबों:


201

विचित्र रूप से यह ऐसा लगता है कि अन्य दो उत्तर इसे स्पष्ट नहीं करते हैं, और यह निश्चित रूप से कहने योग्य है:


i++इसका मतलब है 'मुझे मूल्य बताओ i, फिर वेतन वृद्धि'

++iमतलब 'वेतन वृद्धि i, फिर मुझे मूल्य बताओ'


वे प्री-इन्क्रीमेंट, पोस्ट-इन्क्रीमेंट ऑपरेटर हैं। दोनों ही मामलों में परिवर्तनशील है , लेकिन अगर आप दोनों अभिव्यक्तियों का मूल्य समान मामलों में लेना चाहते हैं, तो परिणाम अलग-अलग होंगे।



65
न तो कथन सही है। अपने पहले कथन पर विचार करें। i ++ का वास्तव में अर्थ है "मूल्य को बचाना, इसे बढ़ाना, इसे इसमें संग्रहीत करना, फिर मुझे मूल सहेजा गया मूल्य बताना"। यानी, वेतन वृद्धि के बाद कहा जाता है , न कि जैसा आपने पहले कहा है। दूसरे कथन पर विचार करें। i ++ का मतलब वास्तव में "मूल्य को बचाना, इसे बढ़ाना, इसे में संग्रहीत करना है, और मुझे बढ़ा हुआ मूल्य बताना है"। आपके द्वारा कहा गया तरीका यह स्पष्ट नहीं करता है कि क्या मूल्य i का मूल्य है, या वह मान जो मुझे सौंपा गया था; वे अलग हो सकते हैं
एरिक लिपर्ट

8
@ फिर भी, यह मुझे आपके उत्तर के साथ प्रतीत होगा, दूसरा कथन स्पष्ट रूप से सही है। हालांकि वह कुछ कदमों को छोड़कर।
इवान कैरोल

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

12
@ सुपरकैट: चर्चा सी # के बारे में है, सी। पर नहीं
थानातोस

428

इस प्रश्न का विशिष्ट उत्तर, दुर्भाग्य से यहां पहले से ही पोस्ट किया गया है, एक यह है कि "शेष संचालन से पहले वेतन वृद्धि" करता है और दूसरा "शेष संचालन के बाद वेतन वृद्धि" करता है। हालाँकि यह सहज रूप से विचार को पार कर जाता है, लेकिन यह कथन पूरी तरह से गलत हैसमय में घटनाओं का क्रम C # में बहुत अच्छी तरह से परिभाषित किया गया है, और यह मज़बूती से ऐसा मामला नहीं है कि ++ के उपसर्ग (++ संस्करण) और पोस्टफ़िक्स (var ++) संस्करण अन्य कार्यों के संबंध में एक अलग क्रम में चीजें करते हैं।

यह आश्चर्यजनक है कि आप इस प्रश्न के बहुत से गलत उत्तर देखेंगे। एक महान कई "अपने आप को सी # सिखाते हैं" किताबें भी गलत हो जाती हैं। साथ ही, C # करने का तरीका यह बताता है कि C कैसे करता है। बहुत से लोग कारण जैसे कि C # और C एक ही भाषा हैं; वो नहीं हैं। मेरी राय में C # में इंक्रीमेंट और डीक्रीमेंट ऑपरेटर्स का डिज़ाइन इन ऑपरेटर्स सी में डिज़ाइन की खामियों से बचा जाता है।

दो प्रश्न हैं, जिन्हें यह निर्धारित करने के लिए उत्तर दिया जाना चाहिए कि वास्तव में उपसर्ग और पोस्टफ़िक्स ++ का संचालन C # में क्या है। पहला सवाल यह है कि परिणाम क्या है? और दूसरा सवाल यह है कि वेतन वृद्धि का दुष्प्रभाव कब होता है?

यह स्पष्ट नहीं है कि किसी भी प्रश्न का उत्तर क्या है, लेकिन इसे देखने के बाद यह वास्तव में काफी सरल है। एक चर x के लिए x ++ और ++ x ठीक क्या करते हैं, इसके लिए मुझे मंत्र दें।

उपसर्ग फॉर्म (++ x) के लिए:

  1. चर का उत्पादन करने के लिए x का मूल्यांकन किया जाता है
  2. चर का मान एक अस्थायी स्थान पर कॉपी किया जाता है
  3. अस्थायी मूल्य एक नया मान उत्पन्न करने के लिए बढ़ा हुआ है (अस्थायी ओवरराइटिंग नहीं!)
  4. नया मान चर में संग्रहीत है
  5. ऑपरेशन का परिणाम नया मान है (यानी अस्थायी का बढ़ा हुआ मूल्य)

उपसर्ग फॉर्म के लिए (x ++):

  1. चर का उत्पादन करने के लिए x का मूल्यांकन किया जाता है
  2. चर का मान एक अस्थायी स्थान पर कॉपी किया जाता है
  3. अस्थायी मूल्य एक नया मान उत्पन्न करने के लिए बढ़ा हुआ है (अस्थायी ओवरराइटिंग नहीं!)
  4. नया मान चर में संग्रहीत है
  5. ऑपरेशन का परिणाम अस्थायी का मूल्य है

ध्यान देने योग्य कुछ बातें:

सबसे पहले, समय में घटनाओं का क्रम दोनों मामलों में समान है । फिर, यह बिल्कुल नहीं है कि समय में घटनाओं का क्रम उपसर्ग और पश्चात के बीच बदल जाता है। यह कहना पूरी तरह से गलत है कि मूल्यांकन अन्य मूल्यांकन से पहले या अन्य मूल्यांकन के बाद होता है। मूल्यांकन दोनों मामलों में ठीक उसी क्रम में होता है जैसा कि आप चरण 1 के माध्यम से देख सकते हैं। केवल अंतर यह है अंतिम चरण - परिणाम अस्थायी, या नए, वृद्धि मूल्य के मूल्य है या नहीं।

आप इसे एक सरल सी # कंसोल ऐप के साथ आसानी से प्रदर्शित कर सकते हैं:

public class Application
{
    public static int currentValue = 0;

    public static void Main()
    {
        Console.WriteLine("Test 1: ++x");
        (++currentValue).TestMethod();

        Console.WriteLine("\nTest 2: x++");
        (currentValue++).TestMethod();

        Console.WriteLine("\nTest 3: ++x");
        (++currentValue).TestMethod();

        Console.ReadKey();
    }
}

public static class ExtensionMethods 
{
    public static void TestMethod(this int passedInValue) 
    {
        Console.WriteLine("Current:{0} Passed-in:{1}",
            Application.currentValue,
            passedInValue);
    }
}

ये रहे नतीजे ...

Test 1: ++x
Current:1 Passed-in:1

Test 2: x++
Current:2 Passed-in:1

Test 3: ++x
Current:3 Passed-in:3

पहले परीक्षण में, आप देख सकते हैं कि दोनों currentValueऔर क्या पारित किया गया थाTestMethod() विस्तार , उसी मूल्य को दिखाते हैं, जैसा कि अपेक्षित था।

हालांकि, दूसरे मामले में, लोग आपको यह बताने की कोशिश करेंगे कि कॉल के बाद होने वाली वेतन वृद्धि होती currentValueहै , लेकिन जैसा कि आप परिणामों से देख सकते हैं, यह कॉल से पहले होता है जैसा कि 'वर्तमान: 2' परिणाम से संकेत मिलता है।TestMethod()

इस स्थिति में, पहले का मान currentValueअस्थायी में संग्रहीत किया जाता है। अगला, उस मूल्य का एक संवर्धित संस्करण वापस संग्रहीत किया जाता है, currentValueलेकिन अस्थायी को छूने के बिना जो अभी भी मूल मूल्य को संग्रहीत करता है। अंत में उस अस्थायी को पारित कर दिया जाता है TestMethod()। यदि वेतन वृद्धि कॉल के बाद हुई है TestMethod()तो यह समान, गैर-वेतन वृद्धि दो बार लिखेगा, लेकिन ऐसा नहीं होता है।

यह ध्यान रखना महत्वपूर्ण है कि दोनों currentValue++और ++currentValueसंचालन से लौटाया गया मान अस्थायी पर आधारित होता है, न कि उस समय जब चर प्रचालन से बाहर होता है, तब चर में संग्रहीत वास्तविक मूल्य।

ऊपर दिए गए संचालन के क्रम में याद करें, पहले दो चरण अस्थायी में चर के तत्कालीन मूल्य को कॉपी करते हैं। यही वह है जो रिटर्न मान की गणना करने के लिए उपयोग किया जाता है; उपसर्ग संस्करण के मामले में, यह है कि प्रत्यय संस्करण के मामले में जबकि अस्थायी मूल्य में वृद्धि हुई है, यह उस मूल्य को सीधे / गैर-संवर्धित है। अस्थायी में प्रारंभिक भंडारण के बाद चर को फिर से नहीं पढ़ा जाता है।

और अधिक सरल रूप से कहें, तो उपसर्ग संस्करण उस मान को लौटाता है जो चर से पढ़ा गया था (यानी अस्थायी का मान) जबकि उपसर्ग संस्करण उस मान को लौटाता है जो चर पर वापस लिखा गया था (यानी अस्थायी का बढ़ा हुआ मूल्य)। न ही चर का मान लौटाएं।

यह समझना महत्वपूर्ण है क्योंकि चर स्वयं अस्थिर हो सकता है और किसी अन्य थ्रेड पर बदल गया है जिसका अर्थ है कि उन कार्यों का वापसी मूल्य चर में संग्रहीत वर्तमान मूल्य से भिन्न हो सकता है।

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

https://ericlippert.com/2009/08/10/precedence-vs-order-redux/

जिसके कारण यह SO प्रश्न:

int [] गिरफ्तारी = {0}; int value = arrest [गिरफ्तारी [0] ++]; मान = 1?

आपको इस विषय पर मेरे पिछले लेखों में भी दिलचस्पी हो सकती है:

https://ericlippert.com/2008/05/23/precedence-vs-associativity-vs-order/

तथा

https://ericlippert.com/2007/08/14/c-and-the-pit-of-despair/

और एक दिलचस्प मामला जहां सी को शुद्धता के बारे में तर्क देना मुश्किल हो जाता है:

https://docs.microsoft.com/archive/blogs/ericlippert/bad-recursion-revisited

इसके अलावा, हम अन्य ऑपरेशनों पर विचार करते समय समान सूक्ष्म मुद्दों में भाग लेते हैं, जिनके साइड इफेक्ट होते हैं, जैसे कि जंजीर सरल कार्य:

https://docs.microsoft.com/archive/blogs/ericlippert/chaining-simple-assignments-is-not-so-simple

और यहाँ क्यों वेतन वृद्धि ऑपरेटरों में परिणाम पर कोई रोचक पोस्ट है मूल्यों बल्कि की तुलना में सी # में चर :

मैं C- जैसी भाषाओं में ++ i ++ क्यों नहीं कर सकता?


4
+1: वास्तव में जिज्ञासु के लिए, क्या आप "C में इन ऑपरेशनों की डिज़ाइन खामियों" से क्या मतलब निकाल सकते हैं?
जस्टिन अर्दिनी

21
@ जस्टिन: मैंने कुछ लिंक जोड़े हैं। लेकिन मूल रूप से: सी में, आपके पास कोई गारंटी नहीं है कि समय में क्या ऑर्डर चीजें होती हैं। एक अनुरूप संकलक किसी भी लानत की बात कर सकता है जब वह एक ही अनुक्रम बिंदु में दो उत्परिवर्तन होता है, और आपको यह बताने की आवश्यकता नहीं है कि आप कुछ ऐसा कर रहे हैं जो कार्यान्वयन-परिभाषित व्यवहार है। यह लोगों को खतरनाक रूप से गैर-योग्य कोड लिखने की ओर ले जाता है जो कुछ संकलक पर काम करता है और दूसरों पर पूरी तरह से कुछ अलग करता है।
एरिक लिपर्ट

14
मुझे कहना होगा कि वास्तव में जिज्ञासु के लिए, यह अच्छा ज्ञान है, लेकिन औसत C # आवेदन के लिए, अन्य उत्तरों में शब्दों के बीच का अंतर और वास्तविक चल रहा सामान भाषा के अमूर्त स्तर से बहुत नीचे है जो वास्तव में बनाता है कोई फर्क नहीं। सी # कोडांतरक नहीं है, और 99.9% बार i++या ++iकोड में उपयोग किया जाता है, पृष्ठभूमि में चल रही चीजें बस यही हैं; पृष्ठभूमि में । मैं सी # लिखता हूं कि इस स्तर पर क्या हो रहा है, ऊपर के स्तर पर चढ़ने के लिए, इसलिए यदि यह वास्तव में आपके सी # कोड के लिए मायने रखता है, तो आप पहले से ही गलत भाषा में हो सकते हैं।
21:39 पर टॉमस असचन

24
@ टॉमा: सबसे पहले, मैं सभी सी # अनुप्रयोगों के बारे में चिंतित हूं , न कि केवल औसत अनुप्रयोगों का द्रव्यमान। गैर-औसत अनुप्रयोगों की संख्या बड़ी है। दूसरा, उन मामलों को छोड़कर कोई फर्क नहीं पड़ता जहां यह फर्क पड़ता है । मुझे इस सामान के बारे में असली कोड में असली कीड़े के आधार पर सवाल मिलते हैं, जो शायद साल में आधा दर्जन बार होता है। तीसरा, मैं आपके बड़े बिंदु से सहमत हूं; ++ का पूरा विचार एक बहुत ही निम्न-स्तरीय विचार है जो आधुनिक कोड में बहुत ही विचित्र लगता है। यह वास्तव में केवल वहाँ है क्योंकि यह सी-जैसी भाषाओं के लिए इस तरह के एक ऑपरेटर के लिए मुहावरेदार है।
एरिक लिपर्ट

11
+1 लेकिन मुझे सच बताना है ... यह वर्षों और वर्षों है कि केवल उपसर्ग ऑपरेटरों का एकमात्र उपयोग जो मैं करता हूं वह पंक्ति में अकेला नहीं है (इसलिए ऐसा नहीं है i++;) for (int i = 0; i < x; i++)... और मैं बहुत बहुत इस की खुशी! (और मैं कभी उपसर्ग ऑपरेटर का उपयोग नहीं करता)। अगर मुझे कुछ लिखना है, जिसे समझने के लिए एक वरिष्ठ प्रोग्रामर को 2 मिनट की आवश्यकता होगी ... खैर ... कोड की एक और पंक्ति लिखना या एक अस्थायी चर पेश करना बेहतर होगा :-) मुझे लगता है कि आपका "लेख" (मैं जीत गया 't call it "answer") vindicates मेरी पसंद :-)
xanatos

23

यदि आपके पास है:

int i = 10;
int x = ++i;

तब xहोगा 11

लेकिन अगर आपके पास:

int i = 10;
int x = i++;

तब xहोगा 10

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

आम तौर पर, मैं उपयोग करना पसंद करता हूं ++i जब तक कि कोई अच्छा कारण न हो। उदाहरण के लिए, लूप लिखते समय, मैं उपयोग करना पसंद करता हूं:

for (int i = 0; i < 10; ++i) {
}

या, अगर मुझे केवल एक चर बढ़ाने की आवश्यकता है, तो मुझे उपयोग करना पसंद है:

++x;

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


2
आप शायद कोड लाइनों को क्रम में रखकर अपने उदाहरणों को स्पष्ट कर सकते हैं - अर्थात, "var x = 10; var y = ++ x;" और "var x = 10; var y = x ++;" बजाय।
टॉमस एशचन

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

3
मैंने इसे iचर नाम के बजाय varइसका C # कीवर्ड के रूप में उपयोग करने के लिए संपादित किया है ।
जेफ येट्स

2
तो बिना चरों को बताए और सिर्फ "i ++;" या "++ मैं?" बिल्कुल वही परिणाम देगा या क्या मैं इसे गलत कर रहा हूं?
डलोर

1
@ एरिक: क्या आप स्पष्ट कर सकते हैं कि मूल शब्द "खतरनाक" क्यों है? यह सही उपयोग की ओर ले जाता है, भले ही वह विवरण गलत हो।
जस्टिन अर्दिनी

7
int i = 0;
Console.WriteLine(i++); // Prints 0. Then value of "i" becomes 1.
Console.WriteLine(--i); // Value of "i" becomes 0. Then prints 0.

क्या इससे आपके सवाल का जवाब मिलता है ?


4
कोई कल्पना कर सकता है कि पोस्टफिक्स इन्क्रीमेंट और प्रीफिक्स डिक्रीमेंट का वैरिएबल पर कोई असर नहीं होता: P
एंड्रियास

5

ऑपरेटर के काम करने का तरीका यह है कि यह एक ही समय में बढ़ जाता है, लेकिन अगर यह एक चर से पहले है, तो अभिव्यक्ति बढ़े / घटाए गए चर के साथ मूल्यांकन करेगी:

int x = 0;   //x is 0
int y = ++x; //x is 1 and y is 1

यदि यह चर के बाद है, तो वर्तमान कथन मूल चर के साथ निष्पादित हो जाएगा, जैसे कि यह अभी तक बढ़ाया / बढ़ाया नहीं गया है:

int x = 0;   //x is 0
int y = x++; //'y = x' is evaluated with x=0, but x is still incremented. So, x is 1, but y is 0

जब तक आवश्यक न हो मैं प्री-इंक्रीमेंट / डिक्रीमेंट (++ x) का उपयोग करके dcp से सहमत हूं। वास्तव में एकमात्र समय जब मैं पोस्ट-इन्क्रीमेंट / डिक्रीमेंट का उपयोग करता हूं, उस समय के लूप या लूप में होता है। ये छोरियां समान हैं:

while (x < 5)  //evaluates conditional statement
{
    //some code
    ++x;       //increments x
}

या

while (x++ < 5) //evaluates conditional statement with x value before increment, and x is incremented
{
    //some code
}

आप एरेज़ को इंडेक्स करते समय भी ऐसा कर सकते हैं और ऐसे:

int i = 0;
int[] MyArray = new int[2];
MyArray[i++] = 1234; //sets array at index 0 to '1234' and i is incremented
MyArray[i] = 5678;   //sets array at index 1 to '5678'
int temp = MyArray[--i]; //temp is 1234 (becasue of pre-decrement);

आदि आदि...


4

सिर्फ C ++ में रिकॉर्ड के लिए, यदि आप या तो (यानी) का उपयोग कर सकते हैं, तो आप परिचालन के आदेश की परवाह नहीं करते हैं (आप बस वेतन वृद्धि या गिरावट चाहते हैं और बाद में इसका उपयोग करना चाहते हैं) उपसर्ग ऑपरेटर अधिक कुशल है क्योंकि यह नहीं करता है ऑब्जेक्ट की एक अस्थायी प्रतिलिपि बनाना होगा। दुर्भाग्य से, ज्यादातर लोग उपसर्ग (++ संस्करण) के बजाय पॉज़िफ़िक्स (var ++) का उपयोग करते हैं, सिर्फ इसलिए कि हमने शुरू में जो सीखा है। (मुझे एक साक्षात्कार में इस बारे में पूछा गया था)। सुनिश्चित नहीं है कि यह सी # में सच है, लेकिन मुझे लगता है कि यह होगा।


2
यही कारण है कि मैं अकेले # का उपयोग करने के लिए c # में उपयोग करता हूं (यानी एक बड़ी अभिव्यक्ति में नहीं)। जब भी मैं i ++ के साथ एसी # लूप देखता हूं, तो मैं थोड़ा रोता हूं, लेकिन अब मुझे पता है कि सी # में यह वास्तव में बहुत ही समान कोड है जो मुझे बेहतर लगता है। व्यक्तिगत रूप से मैं अभी भी ++ i का उपयोग करूंगा क्योंकि आदतें कठिन मर जाती हैं।
मिकले
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.