पोस्टफिक्स इन्क्रीमेंट ऑपरेटर से बचें


25

मैंने पढ़ा है कि मुझे प्रदर्शन कारणों (कुछ मामलों में) के कारण पोस्टफ़िक्स इंक्रीमेंट ऑपरेटर से बचना चाहिए ।

लेकिन क्या यह कोड पठनीयता को प्रभावित नहीं करता है? मेरी राय में:

for(int i = 0; i < 42; i++);
    /* i will never equal 42! */

इससे बेहतर लग रहा है:

for(int i = 0; i < 42; ++i);
    /* i will never equal 42! */

लेकिन यह शायद सिर्फ आदत से बाहर है। बेशक, मैंने कई उपयोग नहीं देखे हैं ++i

क्या इस मामले में पठनीयता का त्याग करना बुरा है? या मैं सिर्फ अंधा हूं, और ++iक्या इससे अधिक पठनीय है i++?


1
मैं प्रयोग किया जाता है i++इससे पहले कि मैं जानता था कि इस पर प्रदर्शन को प्रभावित कर सकता है ++iतो मैं बंद,। पहले तो बाद में थोड़ा अजीब लगा, लेकिन थोड़ी देर के बाद मुझे इसकी आदत हो गई और अब यह उतना ही स्वाभाविक लगता है i++
गाब्लिन

15
++iऔर i++कुछ संदर्भों में अलग-अलग चीजें करें, यह न मानें कि वे समान हैं।
परिक्रमा

2
क्या यह C या C ++ के बारे में है? वे दो बहुत अलग भाषाएं हैं! :-) C ++ में मुहावरेदार लूप है for (type i = 0; i != 42; ++i)। न केवल operator++अतिभारित किया जा सकता है, लेकिन ऐसा कर सकते हैं operator!=और operator<। उपसर्ग वृद्धि, उपसर्ग की तुलना में अधिक महंगी नहीं है, नहीं-बराबर कम-से-अधिक महंगी नहीं है। हमें किन लोगों का उपयोग करना चाहिए?
बो पर्सन

7
क्या इसे ++ C नहीं कहा जाना चाहिए?
आर्मंड

21
@ स्टेपेन: C ++ का अर्थ है C, इसे जोड़ें, और फिर पुराने का उपयोग करें
सुपरकैट

जवाबों:


58

तथ्यों:

  1. i ++ और ++ i को पढ़ना उतना ही आसान है। आपको एक पसंद नहीं है क्योंकि आप इसके लिए अभ्यस्त नहीं हैं, लेकिन अनिवार्य रूप से कुछ भी नहीं है जो आप इसे गलत समझ सकते हैं, इसलिए यह पढ़ने या लिखने के लिए अधिक काम नहीं है।

  2. कम से कम कुछ मामलों में, पोस्टफिक्स ऑपरेटर कम कुशल होगा।

  3. हालाँकि, 99.99% मामलों में, यह कोई फर्क नहीं पड़ेगा क्योंकि (ए) यह वैसे भी एक साधारण या आदिम प्रकार पर काम करेगा और यह केवल एक समस्या है अगर यह एक बड़ी वस्तु (बी) की नकल कर रहा है तो यह प्रदर्शन में नहीं होगा कोड का महत्वपूर्ण हिस्सा (सी) आपको नहीं पता कि कंपाइलर इसे ऑप्टिमाइज़ करेगा या नहीं, यह कर सकता है।

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

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

हालांकि, यहां, मेरा मानना ​​है कि उत्तर सरल है: मेरा मानना ​​है कि उपसर्ग लिखना तब तक है जब तक कि आपको विशेष रूप से पोस्टफ़िक्स की आवश्यकता न हो (ए) बहुत मामूली रूप से स्पष्ट और (बी) बहुत अधिक कुशल होने की संभावना है, इसलिए आपको हमेशा लिखना चाहिए कि डिफ़ॉल्ट रूप से, लेकिन अगर आप भूल जाते हैं तो इसके बारे में चिंता न करें।

छह महीने पहले, मैंने आपके जैसे ही सोचा था, कि i ++ अधिक प्राकृतिक था, लेकिन यह विशुद्ध रूप से आप के लिए उपयोग किया जाता है।

EDIT 1: स्कॉट मेयर्स, "मोर इफेक्टिव सी ++" में, जिसे मैं आमतौर पर इस बात पर भरोसा करता हूं, का कहना है कि आपको उपयोगकर्ता-परिभाषित प्रकारों पर पोस्टफ़िक्स ऑपरेटर का उपयोग करने से बचना चाहिए (क्योंकि पोस्टफ़िक्स इंक्रीमेंट फ़ंक्शन का एकमात्र सामान्य कार्यान्वयन है) ऑब्जेक्ट की कॉपी, इंक्रीमेंट करने के लिए उपसर्ग इंक्रीमेंट फ़ंक्शन को कॉल करें और कॉपी वापस करें, लेकिन कॉपी ऑपरेशन महंगा हो सकता है)।

इसलिए, हम नहीं जानते हैं कि क्या (ए) के बारे में कोई सामान्य नियम हैं जो आज सच है, (बी) कि क्या यह आंतरिक प्रकारों पर भी लागू होता है (कम) तो क्या आपको "++" का उपयोग करना चाहिए एक हल्के पुनरावृत्ति वर्ग की तुलना में अधिक कुछ भी। लेकिन सभी कारणों के लिए जो मैंने ऊपर वर्णित किया है, इससे कोई फर्क नहीं पड़ता है, जो मैंने पहले कहा था वह करो।

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


आपकी पोस्टिंग पैसे पर सही है। ऐसे भावों में जहां infix + ऑपरेटर और पोस्ट-इन्क्रीमेंट ++ को ओवरलैस किया गया है जैसे कि aClassInst = someOtherClassInst + yetAnotherClassInst ++, पोस्ट-इन्क्रिमेंट ऑपरेशन करने के लिए कोड जेनरेट करने से पहले एडिशन ऑपरेशन करने के लिए, कोड को जेनरेट करने की आवश्यकता को पूरा करते हुए, पार्सर कोड जनरेट करेगा। एक अस्थायी प्रतिलिपि बनाएँ। यहाँ प्रदर्शन हत्यारा पोस्ट-इन्क्रीमेंट नहीं है। यह एक अतिभारित infix ऑपरेटर का उपयोग है। Infix ऑपरेटर्स नए उदाहरणों का उत्पादन करते हैं।
बिट-ट्विडलर

2
मुझे इस बात पर बहुत संदेह है कि लोग इस प्रश्न / उत्तर में संदर्भित एक निश्चित लोकप्रिय प्रोग्रामिंग भाषा के नाम के i++बजाय 'का उपयोग' कर रहे हैं ++i...
छाया

61

प्रोग्रामर के लिए हमेशा कोड पहला और कंप्यूटर दूसरा।

यदि प्रदर्शन में कोई अंतर है, तो कंपाइलर ने आपके कोड पर अपनी विशेषज्ञ नज़र डाली है, और आप इसे माप सकते हैं और यह मायने रखता है - तो आप इसे बदल सकते हैं।


7
सुपरब स्टेटमेंट !!!
डेव

8
@ मर्टिन: जो ठीक है इसलिए मैं उपसर्ग वेतन वृद्धि का उपयोग करूंगा। उपसर्ग शब्दार्थ पुराने मूल्य को ध्यान में रखते हुए, और यदि इसकी कोई आवश्यकता नहीं है, तो इसका उपयोग करने के लिए गलत है।
Matthieu M.

1
एक लूप इंडेक्स के लिए जो स्पष्ट होगा - लेकिन यदि आप किसी पॉइंटर पर इंक्रीमेंट करके और किसी उपसर्ग का उपयोग करके पुनरावृत्ति कर रहे थे, तो इसका मतलब था कि एक गैरकानूनी पते पर शुरू होने से पहले एक प्रदर्शन को बढ़ावा देने के लिए जो भी गलत हो,
मार्टिन बेकर

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

2
@ मैथ्यू: मेरे द्वारा पोस्ट किया गया कोड ऑप्टिमाइज़ेशन के साथ जेनरेट किया गया था। C ++ विनिर्देश यह नहीं बताता है कि पोस्ट-इंक्रीमेंट का उपयोग करने पर एक कंपाइलर को एक अस्थायी मान का उत्पादन करना चाहिए। यह केवल पूर्व और पश्चात के संचालकों की पूर्ववर्ती स्थिति बताता है।
बिट-ट्विडलर

13

जीसीसी दोनों छोरों के लिए एक ही मशीन कोड का उत्पादन करता है।

C कोड

int main(int argc, char** argv)
{
    for (int i = 0; i < 42; i++)
            printf("i = %d\n",i);

    for (int i = 0; i < 42; ++i)
        printf("i = %d\n",i);

    return 0;
}

विधानसभा कोड (मेरी टिप्पणियों के साथ)

    cstring
LC0:
    .ascii "i = %d\12\0"
    .text
.globl _main
_main:
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ebx
    subl    $36, %esp
    call    L9
"L00000000001$pb":
L9:
    popl    %ebx
    movl    $0, -16(%ebp)  // -16(%ebp) is "i" for the first loop 
    jmp L2
L3:
    movl    -16(%ebp), %eax   // move i for the first loop to the eax register 
    movl    %eax, 4(%esp)     // push i onto the stack
    leal    LC0-"L00000000001$pb"(%ebx), %eax // load the effective address of the format string into the eax register
    movl    %eax, (%esp)      // push the address of the format string onto the stack
    call    L_printf$stub    // call printf
    leal    -16(%ebp), %eax  // make the eax register point to i
    incl    (%eax)           // increment i
L2:
    cmpl    $41, -16(%ebp)  // compare i to the number 41
    jle L3              // jump to L3 if less than or equal to 41
    movl    $0, -12(%ebp)   // -12(%ebp) is "i" for the second loop  
    jmp L5
L6:
    movl    -12(%ebp), %eax   // move i for the second loop to the eax register 
    movl    %eax, 4(%esp)     // push i onto the stack
    leal    LC0-"L00000000001$pb"(%ebx), %eax // load the effective address of the format string into the eax register
    movl    %eax, (%esp)      // push the address of the format string onto the stack
    call    L_printf$stub     // call printf
    leal    -12(%ebp), %eax  // make eax point to i
    incl    (%eax)           // increment i
L5:
    cmpl    $41, -12(%ebp)   // compare i to 41 
    jle L6               // jump to L6 if less than or equal to 41
    movl    $0, %eax
    addl    $36, %esp
    popl    %ebx
    leave
    ret
    .section __IMPORT,__jump_table,symbol_stubs,self_modifying_code+pure_instructions,5
L_printf$stub:
    .indirect_symbol _printf
    hlt ; hlt ; hlt ; hlt ; hlt
    .subsections_via_symbols

अनुकूलन के बारे में कैसे चालू हुआ?
सर्व-इंक

2
@user: शायद कोई बदलाव नहीं है, लेकिन क्या आप वास्तव में बिट-ट्विडलर से जल्द ही वापस आने की उम्मीद करते हैं?
Deduplicator

2
ध्यान रखें: जबकि C में ओवरलोडेड ऑपरेटरों के साथ कोई उपयोगकर्ता-परिभाषित प्रकार नहीं हैं, C ++ में हैं, और बुनियादी प्रकार से उपयोगकर्ता-परिभाषित प्रकारों के लिए सामान्यीकरण केवल अमान्य है
Deduplicator

@Deduplicator: धन्यवाद, यह इंगित करने के लिए भी कि यह उत्तर उपयोगकर्ता द्वारा परिभाषित प्रकारों के लिए सामान्यीकृत नहीं है। मैंने पूछने से पहले उसके यूजर पेज को नहीं देखा था।
सर्व-इंक

12

प्रदर्शन के बारे में चिंता मत करो, समय का 97% कहते हैं। सभी बुराईयो की जड़ समयपूर्व इष्टतमीकरण है।

- डोनाल्ड नथ

अब जब कि यह हमारे रास्ते से बाहर है, की हमारी पसंद करते हैं sanely :

  • ++i: उपसर्ग वृद्धि , वर्तमान मूल्य वृद्धि और परिणाम देता है
  • i++: पोस्टफिक्स इन्क्रीमेंट , वैल्यू कॉपी करें, करंट वैल्यू बढ़ाएं , कॉपी की पैदावार होती है

जब तक पुराने मूल्य की एक प्रति की आवश्यकता नहीं होती है, तब पोस्टफिक्स इंक्रीमेंट का उपयोग चीजों को प्राप्त करने का एक गोल-गोल तरीका है।

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

भले ही यह (वास्तव में) यहां मामूली है, ऐसे समय हैं जब मुझे कोड पढ़कर वास्तव में आश्चर्य हुआ है: मैं वास्तव में सोच रहा था कि क्या इरादे और वास्तविक एक्सप्रेस मेल खाते थे, और निश्चित रूप से, कुछ महीनों के बाद, वे (या मैं) या तो याद नहीं ...

इसलिए, इससे कोई फर्क नहीं पड़ता कि यह आपके लिए सही है या नहीं। गले KISS । कुछ महीनों में आप अपनी पुरानी प्रथाओं से दूर हो जाएंगे।


4

C ++ में, यदि ऑपरेटर ओवरलोड शामिल हैं, तो आप पर्याप्त प्रदर्शन अंतर बना सकते हैं, खासकर यदि आप टेम्पल कोड लिख रहे हैं और यह नहीं जानते हैं कि इसमें क्या पुनरावृत्तियाँ पास हो सकती हैं। किसी भी इटैलर X के पीछे तर्क पर्याप्त और महत्वपूर्ण दोनों हो सकते हैं- यह संकलक द्वारा धीमा, और अकल्पनीय है।

लेकिन यह सी में ऐसा नहीं है, जहां आप जानते हैं कि यह केवल एक तुच्छ प्रकार होगा, और प्रदर्शन अंतर तुच्छ है और संकलक आसानी से दूर का अनुकूलन कर सकता है।

इसलिए एक टिप: आप C या C ++ में प्रोग्राम करते हैं, और प्रश्न एक या दूसरे से संबंधित होते हैं, दोनों में नहीं।


2

या तो ऑपरेशन का प्रदर्शन अंतर्निहित वास्तुकला पर अत्यधिक निर्भर है। किसी को स्मृति में संग्रहीत मान को बढ़ाना पड़ता है, जिसका अर्थ है कि वॉन न्यूमैन की अड़चन दोनों मामलों में सीमित कारक है।

++ i के मामले में, हमें करना होगा

Fetch i from memory 
Increment i
Store i back to memory
Use i

I ++ के मामले में, हमें करना होगा

Fetch i from memory
Use i
Increment i
Store i back to memory

++ और - ऑपरेटर्स PDP-11 इंस्ट्रक्शन सेट में अपनी उत्पत्ति का पता लगाते हैं। पीडीपी -11 एक रजिस्टर पर स्वत: पश्चात वृद्धि कर सकता है। यह एक रजिस्टर में निहित एक प्रभावी पते पर स्वचालित पूर्व-डिक्रीमेंट भी कर सकता है। या तो मामले में, कंपाइलर केवल इन मशीन-स्तरीय संचालन का लाभ उठा सकता है यदि प्रश्न में चर "रजिस्टर" चर था।


2

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

लेख पढ़ने के बाद, मुझे यह तीन कारणों से बहुत पक्का नहीं लगता। एक, कंपाइलर का उपयोग उस ऑब्जेक्ट के निर्माण के चारों ओर अनुकूलन करने में सक्षम होना चाहिए जो कभी उपयोग नहीं किया जाता है। दो, i++अवधारणा लूप के लिए संख्यात्मक के लिए मुहावरेदार है , इसलिए मैं जिन मामलों को वास्तव में प्रभावित होते हुए देख सकता हूं, वे सीमित हैं। तीन, वे एक विशुद्ध रूप से सैद्धांतिक तर्क प्रदान करते हैं, जिसमें कोई संख्या नहीं है।

विशेष रूप से # 1 के आधार पर, मेरा अनुमान है कि जब आप वास्तव में टाइमिंग करेंगे, तो वे एक-दूसरे के ठीक बगल में होंगे।


-1

सबसे पहले यह पठनीयता IMO को प्रभावित नहीं करता है। यह वह नहीं है जो आपके देखने के लिए उपयोग किया जाता है बल्कि यह केवल एक छोटा समय होगा जब आप इसके आदी हो जाते हैं।

जब तक आप अपने कोड में एक टन पोस्टफिक्स ऑपरेटरों का उपयोग नहीं करते हैं, तब तक आप संभवतः बहुत अंतर नहीं देख सकते हैं। जब संभव हो तो उनका उपयोग नहीं करने के लिए मुख्य तर्क यह है कि मूल संस्करण के मूल्य की एक प्रति को उन तर्कों के अंत तक रखा जाना चाहिए जहां मूल संस्करण अभी भी उपयोग किया जा सकता है। आर्किटेक्चर के आधार पर यह 32बिट्स या 64 बिट्स है। यह 4 या 8 बाइट्स या 0.00390625 या 0.0078125 एमबी के बराबर है। संभावना बहुत अधिक है कि जब तक आप उनमें से एक टन का उपयोग नहीं कर रहे हैं, जिसे बहुत लंबे समय तक सहेजने की आवश्यकता होती है, जो कि आज के कंप्यूटर संसाधनों और गति के साथ आपको पोस्टफ़िक्स से प्रीफ़िक्स के लिए स्विच करने में भी अंतर नहीं दिखाई देगा।

संपादित करें: इस शेष भाग को भूल जाएं क्योंकि मेरा निष्कर्ष गलत साबित हुआ (++ i और i ++ के हिस्से को छोड़कर हमेशा एक ही काम नहीं करता ... यह अभी भी सच है)।

इसके अलावा यह पहले बताया गया था कि वे एक मामले में एक ही काम नहीं करते हैं। यदि आप निर्णय लेते हैं तो स्विच बनाने के बारे में सावधान रहें। मैंने इसे कभी भी आज़माया नहीं है (मैंने हमेशा पोस्टफ़िक्स का इस्तेमाल किया है) इसलिए मुझे यकीन नहीं है, लेकिन मुझे लगता है कि पोस्टफ़िक्स से प्रीफ़िक्स में बदलने के परिणामस्वरूप अलग-अलग परिणाम होंगे: (फिर से मैं गलत हो सकता है ... कंपाइलर पर निर्भर करता है (दुभाषिया भी)

for (int i=0; i < 10; i++) //the set of i values here will be {0,1,2,3,4,5,6,7,8,9}
for (int i=0; i < 10; ++i) //the set of i values here will be {1,2,3,4,5,6,7,8,9,10}

4
वेतन वृद्धि का कार्य लूप के अंत में होता है, इसलिए उनका सटीक समान आउटपुट होगा। यह संकलक / दुभाषिया पर निर्भर नहीं है।
१२:२४ पर jsternberg

@jsternberg ... धन्यवाद मुझे यकीन नहीं था कि जब वेतन वृद्धि हुई थी, तो मैं वास्तव में कभी इसे बाहर जाने के लिए एक कारण नहीं था। जब से मैंने कॉलेज में कम्पाइलर किया तब से यह बहुत लंबा है! lol
केनेथ

गलत गलत है।
रूहोला

-1

मुझे लगता है कि शब्दार्थ से, ++iअधिक समझ में आता है i++, इसलिए मैं पहले एक से चिपक जाता हूं, सिवाय इसके कि ऐसा न करना आम है (जैसे जावा में, जहां आपको इसका उपयोग करना चाहिए i++क्योंकि यह व्यापक रूप से उपयोग किया जाता है)।


-2

यह केवल प्रदर्शन के बारे में नहीं है।

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

और आदिम प्रकार और जटिल प्रकारों के लिए विभिन्न वेतन वृद्धि का उपयोग करना ... यह वास्तव में अपठनीय है।


-2

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

इसलिए, मैं @martin beckett के साथ पूर्ण हार्दिक सहमत हूं: इसे अपने लिए आसान बनाएं, यह पहले से ही काफी कठिन है।

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