C ++ में इंक्रीमेंट करना - x ++ या ++ x का उपयोग कब करना है?


91

मैं वर्तमान में C ++ सीख रहा हूं और मैंने कुछ समय पहले वेतन वृद्धि के बारे में सीखा है। मुझे पता है कि आप "++ x" का उपयोग करने से पहले वृद्धि करने के लिए कर सकते हैं और बाद में करने के लिए "x ++"।

फिर भी, मैं वास्तव में नहीं जानता कि दोनों में से किसी का उपयोग कब करना है ... मैंने वास्तव में "++ x" का उपयोग कभी नहीं किया है और चीजें हमेशा अभी तक ठीक काम करती हैं - इसलिए, मुझे इसका उपयोग कब करना चाहिए?

उदाहरण: लूप के लिए, जब "++ x" का उपयोग करना बेहतर होता है?

इसके अलावा, क्या कोई यह बता सकता है कि विभिन्न वेतन वृद्धि (या गिरावट) कैसे काम करती है? मुझे वास्तव में इसकी प्रशंसा करनी होगी।

जवाबों:


112

यह वरीयता का सवाल नहीं है, बल्कि तर्क का है।

x++वर्तमान कथन को संसाधित करने के बाद चर x का मान बढ़ाता है ।

++xवर्तमान कथन को संसाधित करने से पहले चर x का मान बढ़ाता है ।

इसलिए आप जो भी तर्क लिखेंगे, उस पर निर्णय लें।

x += ++iवेतन वृद्धि होगी और मैं x में 1 + जोड़ दूंगा। x += i++मैं x को जोड़ दूंगा, फिर वेतन वृद्धि i।


27
और कृपया ध्यान दें कि प्राइमरों में लूप के लिए, बिल्कुल कोई अंतर नहीं है। कई कोडिंग शैलियों की सिफारिश होगी कि एक वेतन वृद्धि ऑपरेटर का उपयोग न करें जहां इसे गलत समझा जा सकता है; यानी, x ++ या ++ x केवल अपनी लाइन पर मौजूद होना चाहिए, कभी भी y = x ++ के रूप में नहीं। व्यक्तिगत रूप से, मुझे यह पसंद नहीं है, लेकिन यह असामान्य है
16

2
और अगर इसकी अपनी लाइन पर उपयोग किया जाता है, तो उत्पन्न कोड लगभग समान होना निश्चित है।
नोसरेडना

14
यह पेडेंट्री की तरह लग सकता है (मुख्यतः क्योंकि यह :) है) लेकिन सी ++ में, वेतन वृद्धि x++से xपहले x++के मूल्य के साथ एक प्रतिद्वंद्विता है, xएक वेतन वृद्धि के बाद के मूल्य के साथ एक अंतराल है । न तो अभिव्यक्ति की गारंटी देता है जब वास्तविक वृद्धि मूल्य वापस एक्स पर संग्रहीत किया जाता है, यह केवल गारंटी है कि यह अगले अनुक्रम बिंदु से पहले होता है। 'वर्तमान कथन को संसाधित करने के बाद' कड़ाई से सटीक नहीं है क्योंकि कुछ अभिव्यक्तियों में अनुक्रम बिंदु हैं और कुछ कथन यौगिक कथन हैं।
सीबी बेली

10
दरअसल, इसका जवाब भ्रामक है। उस समय बिंदु जहां चर x को संशोधित किया गया है, शायद अभ्यास में भिन्न नहीं है। अंतर यह है कि x ++ को x के पिछले मान का एक अंतराल लौटाने के लिए परिभाषित किया गया है, जबकि ++ x अभी भी चर x को संदर्भित करता है।
बेच दें

5
@ बियोवुल्फ़: उत्तर का अर्थ है एक आदेश जो मौजूद नहीं है। जब वेतन वृद्धि होती है, तो कहने के लिए मानक में कुछ भी नहीं होता है। संकलक "x + = i ++" को लागू करने का हकदार है: int j = i; i = i + 1; x + = j? "(अर्थात।" मैं "वर्तमान कथन को संसाधित करने से पहले" बढ़ाता हूं)। यही कारण है कि "i = i ++" में अपरिभाषित व्यवहार है और इसके कारण मुझे लगता है कि उत्तर को "ट्विक करने" की आवश्यकता है। "x" का विवरण। + = ++ i "सही है क्योंकि आदेश का कोई सुझाव नहीं है:" मैं वेतन वृद्धि करेगा और i + 1 को x में जोड़ देगा "
रिचर्ड कॉर्डन

53

स्कॉट मेयर्स आपको उन अवसरों को छोड़कर उपसर्ग पसंद करना बताता है, जहां तर्क यह तय करेगा कि उपसर्ग उपयुक्त है।

"अधिक प्रभावी C ++" आइटम # 6 - मेरे लिए पर्याप्त अधिकार है।

उन लोगों के लिए जो किताब के मालिक नहीं हैं, यहाँ प्रासंगिक उद्धरण हैं। पृष्ठ 32 से:

सी प्रोग्रामर के रूप में आपके दिनों से, आपको याद हो सकता है कि वेतन वृद्धि ऑपरेटर के उपसर्ग रूप को कभी-कभी "वेतन वृद्धि" कहा जाता है, जबकि पोस्टफ़िक्स फॉर्म को अक्सर "भ्रूण और वेतन वृद्धि" के रूप में जाना जाता है। दो वाक्यांशों को याद रखना महत्वपूर्ण है, क्योंकि वे सभी लेकिन औपचारिक विनिर्देशों के रूप में कार्य करते हैं ...

और पेज 34 पर:

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


4
यदि कंपाइलर को यह महसूस नहीं होता है कि वेतन वृद्धि से पहले का मूल्य अनुपयोगी है, तो यह कई निर्देशों में पोस्टफिक्स वेतन वृद्धि को लागू कर सकता है - पुराने मान की प्रतिलिपि बनाएँ, और फिर वेतन वृद्धि। उपसर्ग वृद्धि हमेशा एक निर्देश होना चाहिए।
gnud

8
मैं कल इस परीक्षण को gcc के साथ हुआ: एक लूप के लिए जिसमें मान को निष्पादित करने के बाद फेंक दिया जाता है i++या ++i, उत्पन्न कोड समान होता है।
जियोर्जियो

लूप के लिए इसे बाहर की कोशिश करें। एक असाइनमेंट में व्यवहार अलग होना चाहिए।
duffymo

मैं अपने दूसरे बिंदु पर स्कॉट मेयर्स के साथ स्पष्ट रूप से असहमत हूं - यह आमतौर पर अप्रासंगिक है क्योंकि "x ++" या "++ x" के 90% से अधिक मामले आमतौर पर किसी भी असाइनमेंट से पृथक होते हैं, और ऑप्टिमाइज़र यह पहचानने के लिए पर्याप्त स्मार्ट हैं कि किसी अस्थायी चर की आवश्यकता नहीं है ऐसे मामलों में बनाया जाए। उस स्थिति में, दो रूप पूरी तरह से विनिमेय हैं। इसका निहितार्थ यह है कि "x ++" के साथ पुराने कोड बेस को केवल छोड़ दिया जाना चाहिए - आपको कहीं भी प्रदर्शन में सुधार करने की तुलना में सूक्ष्म त्रुटियों को "++ x" में बदलने की अधिक संभावना है। संभवतः "x ++" का उपयोग करना और लोगों को सोचने के लिए बेहतर है।
ओमताई

2
आप स्कॉट मेयर्स पर भरोसा कर सकते हैं जो आप चाहते हैं, लेकिन अगर आपका कोड इतना प्रदर्शन-निर्भर है कि किसी भी प्रदर्शन अंतर ++xऔर x++वास्तव में मायने रखता है, तो यह बहुत अधिक महत्वपूर्ण है कि आप वास्तव में एक संकलक का उपयोग करें जो पूरी तरह से और ठीक से संस्करण का अनुकूलन कर सकता है चाहे कोई भी संस्करण हो संदर्भ। "चूंकि मैं इस भद्दे पुराने हथौड़ा का उपयोग कर रहा हूं, मैं केवल 43.7 डिग्री के कोण पर नाखून चला सकता हूं" केवल 43.7 डिग्री पर नाखून चलाकर घर बनाने के लिए एक खराब तर्क है। एक बेहतर उपकरण का उपयोग करें।
एंड्रयू हेनल

28

से cppreference जब iterators incrementing:

यदि आप पुराने मूल्य का उपयोग नहीं करने जा रहे हैं तो आपको पोस्ट-इन्क्रीमेंट ऑपरेटर (iter ++) को प्री-इन्क्रीमेंट ऑपरेटर (++ iter) पसंद करना चाहिए। वेतन वृद्धि आम तौर पर निम्नानुसार लागू की जाती है:

   Iter operator++(int)   {
     Iter tmp(*this); // store the old value in a temporary object
     ++*this;         // call pre-increment
     return tmp;      // return the old value   }

जाहिर है, यह पूर्व वेतन वृद्धि की तुलना में कम कुशल है।

पूर्व-वृद्धि अस्थायी वस्तु उत्पन्न नहीं करती है। यह एक महत्वपूर्ण अंतर बना सकता है यदि आपकी वस्तु बनाने के लिए महंगी है।


8

मैं सिर्फ यह नोटिस करना चाहता हूं कि यदि आप प्री / पोस्ट इंक्रीमेंट का उपयोग करते हैं, जहां सिमेंटिक (प्री / पोस्ट का) कोई फर्क नहीं पड़ता है, तो जेनिटेड कोड वही है।

उदाहरण:

pre.cpp:

#include <iostream>

int main()
{
  int i = 13;
  i++;
  for (; i < 42; i++)
    {
      std::cout << i << std::endl;
    }
}

post.cpp:

#include <iostream>

int main()
{

  int i = 13;
  ++i;
  for (; i < 42; ++i)
    {
      std::cout << i << std::endl;
    }
}

_

$> g++ -S pre.cpp
$> g++ -S post.cpp
$> diff pre.s post.s   
1c1
<   .file   "pre.cpp"
---
>   .file   "post.cpp"

5
पूर्णांक की तरह एक आदिम प्रकार के लिए, हाँ। क्या आपने यह देखने के लिए जाँच की है कि किसी चीज़ के लिए अंतर क्या है std::map::iterator? बेशक वहाँ दोनों ऑपरेटर अलग-अलग हैं, लेकिन मुझे इस बात की उत्सुकता है कि क्या कंपाइलर पोस्टफ़िक्स को प्रीफ़िक्स के लिए ऑप्टिमाइज़ करेगा अगर रिजल्ट का इस्तेमाल नहीं किया गया। मुझे नहीं लगता कि इसकी अनुमति है - यह देखते हुए कि पोस्टफ़िक्स संस्करण में दुष्प्रभाव हो सकते हैं।
सेह

इसके अलावा, ' कंपाइलर को शायद यह पता चलेगा कि आपको साइड इफेक्ट की जरूरत नहीं है और इसे दूर करना है ' ऐसा स्लोप कोड लिखने का बहाना नहीं होना चाहिए जो बिना किसी कारण के अधिक जटिल पोस्टफिक्स ऑपरेटर्स का उपयोग करता है, जो कि इस तथ्य से अलग है। माना शिक्षण सामग्री बिना किसी स्पष्ट कारण के पोस्टफ़िक्स का उपयोग करती हैं और थोक के आसपास कॉपी की जाती हैं।
अंडरस्कोर_ड

6

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

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

for (int i(0);i<10;++i)
for (int i(0);i<10;i++)

4

मैं @BeowulfOF से सहमत हूँ, हालाँकि स्पष्टता के लिए मैं हमेशा बयानों को विभाजित करने की वकालत करूँगा ताकि तर्क बिल्कुल सही हो, अर्थात:

i++;
x += i;

या

x += i;
i++;

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


2

बस फिर से जोर देना चाहता था कि ++ x , x ++ की तुलना में तेज़ होने की उम्मीद है , (खासकर अगर x कुछ मनमाने प्रकार की वस्तु है), इसलिए जब तक तार्किक कारणों के लिए आवश्यक नहीं है, ++ x का उपयोग किया जाना चाहिए।


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

1

आपने अंतर को सही ढंग से समझाया। यह सिर्फ इस बात पर निर्भर करता है कि क्या आप x को लूप के माध्यम से हर रन से पहले बढ़ाना चाहते हैं, या उसके बाद। यह आपके प्रोग्राम लॉजिक पर निर्भर करता है कि क्या उचित है।

STL-Iterators (जो इन ऑपरेटरों को भी लागू करते हैं) के साथ काम करते समय एक महत्वपूर्ण अंतर यह है कि यह ++ उस वस्तु की एक प्रति बनाता है जो पुनरावृत्ति करने वाले को इंगित करता है, फिर वेतन वृद्धि करता है, और फिर प्रतिलिपि लौटाता है। ++ यह दूसरी तरफ पहले वेतन वृद्धि करता है और फिर उस वस्तु का संदर्भ देता है जिसे पुनरावृत्त अब इंगित करता है। यह ज्यादातर तब ही प्रासंगिक होता है जब प्रदर्शन का हर हिस्सा मायने रखता है या जब आप अपने स्वयं के एसटीएल-इट्रेटर को लागू करते हैं।

संपादित करें: उपसर्ग और प्रत्यय संकेतन का मिश्रण तय किया


लूप की पुनरावृत्ति "पहले / बाद" की बात केवल तभी सार्थक होती है जब स्थिति में पूर्व / पोस्ट inc / decrement होता है। अधिक बार, यह निरंतरता खंड में होगा, जहां यह किसी भी तर्क को नहीं बदल सकता है, यद्यपि यह वर्ग प्रकारों के लिए पोस्टफिक्स का उपयोग करने के लिए धीमा हो सकता है और लोगों को बिना कारण के इसका उपयोग नहीं करना चाहिए।
अंडरस्कोर_ड

1

++ का पोस्टफिक्स फॉर्म, - ऑपरेटर नियम का पालन करता है उपयोग करता है-तब-परिवर्तन ,

उपसर्ग प्रपत्र (++ x, - x) नियम परिवर्तन-फिर-उपयोग का अनुसरण करता है

उदाहरण 1:

जब कई मानों को कैस्केड किया जाता है << कॉट का उपयोग करके, तब गणना (यदि कोई हो) दाएं-बाएं से होती है, लेकिन मुद्रण बाएं से दाएं उदाहरण के लिए होता है, (यदि वैल शुरू में 10)

 cout<< ++val<<" "<< val++<<" "<< val;

में परिणाम होगा

12    10    10 

उदाहरण 2:

टर्बो सी ++ में, ++ की कई घटनाएं (या किसी भी रूप में) एक अभिव्यक्ति में पाई जाती हैं, तो सबसे पहले सभी उपसर्ग रूपों की गणना की जाती है, फिर अभिव्यक्ति का मूल्यांकन किया जाता है और अंत में पोस्टफिक्स फॉर्म की गणना की जाती है जैसे,

int a=10,b;
b=a++ + ++a + ++a + a;
cout<<b<<a<<endl;

यह टर्बो सी ++ में आउटपुट होगा

48 13

जबकि यह आधुनिक दिन संकलक में उत्पादन होगा (क्योंकि वे नियमों का सख्ती से पालन करते हैं)

45 13
  • नोट: एक अभिव्यक्ति में एक ही चर पर वृद्धिशील / वेतन वृद्धि ऑपरेटरों के एकाधिक उपयोग की सिफारिश नहीं की जाती है। ऐसे
    भावों की हैंडलिंग / परिणाम संकलक से संकलक में भिन्न होते हैं।

ऐसा नहीं है कि कई inc / decrement ऑपरेशंस वाले एक्सप्रेशंस "कंपाइलर से कंपाइलर में भिन्न होते हैं", बल्कि इससे भी बदतर हैं: सीक्वेंस पॉइंट्स के बीच ऐसे कई संशोधनों ने अपरिभाषित व्यवहार और कार्यक्रम को जहर दिया है।
अंडरस्कोर_ड

0

कोड की स्पष्टता पर विचार करते समय भाषा सिंटैक्स को समझना महत्वपूर्ण है। एक चरित्र स्ट्रिंग की नकल करने पर विचार करें, उदाहरण के लिए वेतन वृद्धि के बाद:

char a[256] = "Hello world!";
char b[256];
int i = 0;
do {
  b[i] = a[i];
} while (a[i++]);

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

int i = -1;
do {
  ++i;
  b[i] = a[i];
} while (a[i]);

यह स्वाद का मामला है जो स्पष्ट है और अगर मशीन में रजिस्टरों का एक हैंडफुल है, तो दोनों को समान निष्पादन समय होना चाहिए, भले ही [i] एक ऐसा फ़ंक्शन है जो महंगा है या इसके साइड-इफेक्ट्स हैं। एक महत्वपूर्ण अंतर सूचकांक का निकास मूल्य हो सकता है।

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