c - सी में इन संरचनाओं(++ का उपयोग) अनिर्धारित व्यवहार क्यों हैं?




increment undefined-behavior (9)

अक्सर यह प्रश्न कोड से संबंधित प्रश्नों के डुप्लिकेट के रूप में जुड़ा हुआ है

printf("%d %d\n", i, i++);

या

printf("%d %d\n", ++i, i++);

या समान रूपों।

हालांकि यह पहले से बताए गए अपरिभाषित व्यवहार भी है, जबकि सूक्ष्म मतभेद हैं जब printf() एक कथन की तुलना करते समय शामिल है जैसे कि:

   x = i++ + i++;

निम्नलिखित कथन में:

printf("%d %d\n", ++i, i++);

printf() में तर्कों के मूल्यांकन का क्रम unspecified । इसका मतलब है, किसी भी क्रम में अभिव्यक्ति i++ और ++i मूल्यांकन किया जा सकता है। सी 11 मानक इस पर कुछ प्रासंगिक विवरण है:

अनुलग्नक जे, अनिर्दिष्ट व्यवहार

ऑर्डर में फ़ंक्शन डिज़ाइनर, तर्क, और उप-अभिव्यक्तियों का क्रम फ़ंक्शन कॉल (6.5.2.2) में मूल्यांकन किया जाता है।

3.4.4, अनिर्दिष्ट व्यवहार

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

उदाहरण अनिर्दिष्ट व्यवहार का एक उदाहरण वह क्रम है जिसमें किसी फ़ंक्शन के तर्कों का मूल्यांकन किया जाता है।

अनिर्दिष्ट व्यवहार स्वयं एक मुद्दा नहीं है। इस उदाहरण पर विचार करें:

printf("%d %d\n", ++x, y++);

इसमें भी अनिर्दिष्ट व्यवहार है क्योंकि ++x और y++ के मूल्यांकन का क्रम निर्दिष्ट नहीं है। लेकिन यह पूरी तरह से कानूनी और वैध बयान है। इस कथन में कोई अपरिभाषित व्यवहार नहीं है । क्योंकि संशोधनों ( ++x और y++ ) अलग-अलग ऑब्जेक्ट्स के लिए किए जाते हैं।

निम्नलिखित कथन क्या प्रस्तुत करता है

printf("%d %d\n", ++i, i++);

क्योंकि अपरिभाषित व्यवहार यह तथ्य है कि ये दो अभिव्यक्ति एक ही ऑब्जेक्ट को एक हस्तक्षेप अनुक्रम बिंदु के बिना संशोधित करती हैं।

एक और विवरण यह है कि printf () कॉल में शामिल कॉमा एक विभाजक है , न कि अल्पविराम ऑपरेटर

यह एक महत्वपूर्ण भेद है क्योंकि अल्पविराम ऑपरेटर अपने ऑपरेटरों के मूल्यांकन के बीच एक अनुक्रम बिंदु प्रस्तुत करता है, जो निम्नलिखित कानूनी बनाता है:

int i = 5;
int j;

j = (++i, i++);  // No undefined behaviour here because the comma operator 
                 // introduces a sequence point between '++i' and 'i++'

printf("i=%d j=%d\n",i, j); // prints: i=7 j=6

कॉमा ऑपरेटर अपने ऑपरेटरों को बाएं से दाएं का मूल्यांकन करता है और केवल अंतिम ऑपरेंड का मूल्य उत्पन्न करता है। तो j = (++i, i++); , ++i 6 से बढ़ता i और i++ i ( 6 ) का पुराना मान उत्पन्न करता है जो j को सौंपा गया है। फिर i वृद्धि के बाद 7 हो जाता i

तो अगर फंक्शन कॉल में कॉमा तब कॉमा ऑपरेटर बनना था

printf("%d %d\n", ++i, i++);

एक समस्या नहीं होगी। लेकिन यह अपरिभाषित व्यवहार का आह्वान करता है क्योंकि यहां अल्पविराम एक विभाजक है

उन लोगों के लिए जो अपरिभाषित व्यवहार के लिए नए हैं, उन्हें पढ़ने से फायदा होगा कि प्रत्येक सी प्रोग्रामर को अवधारणा को समझने के लिए अनिश्चित व्यवहार के बारे में क्या पता होना चाहिए और सी में अपरिभाषित व्यवहार के कई अन्य रूपों को समझना चाहिए

यह पोस्ट: अनिर्धारित, अनिर्दिष्ट और कार्यान्वयन-परिभाषित व्यवहार भी प्रासंगिक है।

#include <stdio.h>

int main(void)
{
   int i = 0;
   i = i++ + ++i;
   printf("%d\n", i); // 3

   i = 1;
   i = (i++);
   printf("%d\n", i); // 2 Should be 1, no ?

   volatile int u = 0;
   u = u++ + ++u;
   printf("%d\n", u); // 1

   u = 1;
   u = (u++);
   printf("%d\n", u); // 2 Should also be one, no ?

   register int v = 0;
   v = v++ + ++v;
   printf("%d\n", v); // 3 (Should be the same as u ?)

   int w = 0;
   printf("%d %d %d\n", w++, ++w, w); // shouldn't this print 0 2 2

   int x[2] = { 5, 8 }, y = 0;
   x[y] = y ++;
   printf("%d %d\n", x[0], x[1]); // shouldn't this print 0 8? or 5 0?
}

अनुक्रम बिंदुओं और अनिर्धारित व्यवहार के आर्केन विवरण में फंसने के बजाय, इसका जवाब देने का एक और तरीका यह है कि वे क्या पूछना चाहते हैं? प्रोग्रामर क्या करने की कोशिश कर रहा था?

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

दूसरा खंड, i = i++ , समझने में थोड़ा आसान है। कोई स्पष्ट रूप से मुझे बढ़ाने की कोशिश कर रहा है, और परिणाम को वापस सौंपा गया है। लेकिन सी में ऐसा करने के कुछ तरीके हैं। मैं 1 को जोड़ने का सबसे बुनियादी तरीका, और परिणाम को वापस सौंपना, लगभग किसी भी प्रोग्रामिंग भाषा में समान है:

i = i + 1

सी, ज़ाहिर है, एक आसान शॉर्टकट है:

i++

इसका मतलब है, "मुझे 1 जोड़ें, और परिणाम को वापस सौंपें"। तो अगर हम लिखकर, दोनों के एक छिपाने का निर्माण करते हैं

i = i++

हम वास्तव में क्या कह रहे हैं "मैं 1 जोड़ता हूं, और परिणाम को वापस सौंपता हूं, और परिणाम को वापस सौंपता हूं"। हम उलझन में हैं, इसलिए अगर संकलक भ्रमित हो जाता है तो यह मुझे बहुत परेशान नहीं करता है।

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

हम comp.lang.c पर अनगिनत घंटे बिताते थे जैसे अभिव्यक्तियों पर चर्चा करते हुए और वे अपरिभाषित क्यों होते हैं। मेरे दो लंबे उत्तर, जो वास्तव में व्याख्या करने की कोशिश करते हैं, क्यों वेब पर हैं http://www.eskimo.com/~scs/readings/undef.950321.html और http://www.eskimo.com/~scs/readings/precvsooe.960725.html


कारण यह है कि कार्यक्रम अपरिभाषित व्यवहार चला रहा है। समस्या मूल्यांकन आदेश में निहित है, क्योंकि सी ++ 98 मानक के अनुसार कोई अनुक्रम बिंदु आवश्यक नहीं है (सी ++ 11 शब्दावली के अनुसार किसी भी ऑपरेशन को पहले या बाद में अनुक्रमित नहीं किया जाता है)।

हालांकि यदि आप एक कंपाइलर से चिपके रहते हैं, तो आप व्यवहार को लगातार तब तक पाएंगे, जब तक आप फ़ंक्शन कॉल या पॉइंटर्स नहीं जोड़ते, जिससे व्यवहार अधिक गन्दा हो जाता है।

  • तो सबसे पहले जीसीसी: न्यूवेन मिनजीडब्ल्यू 15 जीसीसी 7.1 का उपयोग करके आप पाएंगे:

    #include<stdio.h>
    int main(int argc, char ** argv)
    {
    int i = 0;
    i = i++ + ++i;
    printf("%d\n", i); // 2
    
    i = 1;
    i = (i++);
    printf("%d\n", i); //1
    
    volatile int u = 0;
    u = u++ + ++u;
    printf("%d\n", u); // 2
    
    u = 1;
    u = (u++);
    printf("%d\n", u); //1
    
    register int v = 0;
    v = v++ + ++v;
    printf("%d\n", v); //2
    

    }

जीसीसी कैसे काम करता है? यह दाएं हाथ की ओर (आरएचएस) के लिए बाएं से दाएं क्रम में उप अभिव्यक्तियों का मूल्यांकन करता है, फिर बाएं हाथ की ओर (एलएचएस) मान को असाइन करता है। यह बिल्कुल ठीक है कि कैसे जावा और सी # व्यवहार करते हैं और अपने मानकों को परिभाषित करते हैं। (हां, जावा और सी # में समकक्ष सॉफ़्टवेयर ने व्यवहार को परिभाषित किया है)। यह प्रत्येक उप अभिव्यक्ति का मूल्यांकन आरएचएस स्टेटमेंट में एक से बाएं से दाएं क्रम में करता है; प्रत्येक उप अभिव्यक्ति के लिए: ++ c (पूर्व-वृद्धि) का मूल्यांकन पहले किया जाता है तो मान सी ऑपरेशन के लिए उपयोग किया जाता है, फिर पोस्ट वृद्धि सी ++)।

जीसीसी सी ++ के अनुसार : ऑपरेटर

जीसीसी सी ++ में, ऑपरेटरों की प्राथमिकता उस क्रम को नियंत्रित करती है जिसमें व्यक्तिगत ऑपरेटरों का मूल्यांकन किया जाता है

जीसीसी के रूप में परिभाषित व्यवहार सी ++ में बराबर कोड समझता है:

#include<stdio.h>
int main(int argc, char ** argv)
{
    int i = 0;
    //i = i++ + ++i;
    int r;
    r=i;
    i++;
    ++i;
    r+=i;
    i=r;
    printf("%d\n", i); // 2

    i = 1;
    //i = (i++);
    r=i;
    i++;
    i=r;
    printf("%d\n", i); // 1

    volatile int u = 0;
    //u = u++ + ++u;
    r=u;
    u++;
    ++u;
    r+=u;
    u=r;
    printf("%d\n", u); // 2

    u = 1;
    //u = (u++);
    r=u;
    u++;
    u=r;
    printf("%d\n", u); // 1

    register int v = 0;
    //v = v++ + ++v;
    r=v;
    v++;
    ++v;
    r+=v;
    v=r;
    printf("%d\n", v); //2
}

फिर हम विजुअल स्टूडियो पर जाते हैं । विजुअल स्टूडियो 2015, आपको मिलता है:

#include<stdio.h>
int main(int argc, char ** argv)
{
    int i = 0;
    i = i++ + ++i;
    printf("%d\n", i); // 3

    i = 1;
    i = (i++);
    printf("%d\n", i); // 2 

    volatile int u = 0;
    u = u++ + ++u;
    printf("%d\n", u); // 3

    u = 1;
    u = (u++);
    printf("%d\n", u); // 2 

    register int v = 0;
    v = v++ + ++v;
    printf("%d\n", v); // 3 
}

How does visual studio work, it takes another approach, it evaluates all pre-increments expressions in first pass, then uses variables values in the operations in second pass, assign from RHS to LHS in third pass, then at last pass it evaluates all the post-increment expressions in one pass.

So the equivalent in defined behavior C++ as Visual C++ understands:

#include<stdio.h>
int main(int argc, char ** argv)
{
    int r;
    int i = 0;
    //i = i++ + ++i;
    ++i;
    r = i + i;
    i = r;
    i++;
    printf("%d\n", i); // 3

    i = 1;
    //i = (i++);
    r = i;
    i = r;
    i++;
    printf("%d\n", i); // 2 

    volatile int u = 0;
    //u = u++ + ++u;
    ++u;
    r = u + u;
    u = r;
    u++;
    printf("%d\n", u); // 3

    u = 1;
    //u = (u++);
    r = u;
    u = r;
    u++;
    printf("%d\n", u); // 2 

    register int v = 0;
    //v = v++ + ++v;
    ++v;
    r = v + v;
    v = r;
    v++;
    printf("%d\n", v); // 3 
}

as Visual Studio documentation states at Precedence and Order of Evaluation :

Where several operators appear together, they have equal precedence and are evaluated according to their associativity. The operators in the table are described in the sections beginning with Postfix Operators.


कोड की अपनी लाइन को संकलित और डिस्सेबल करें, अगर आप यह जानना चाहते हैं कि आपको यह मिल रहा है कि आप क्या प्राप्त कर रहे हैं।

यही वह है जो मैं अपनी मशीन पर प्राप्त करता हूं, साथ ही जो मुझे लगता है कि:

$ cat evil.c
void evil(){
  int i = 0;
  i+= i++ + ++i;
}
$ gcc evil.c -c -o evil.bin
$ gdb evil.bin
(gdb) disassemble evil
Dump of assembler code for function evil:
   0x00000000 <+0>:   push   %ebp
   0x00000001 <+1>:   mov    %esp,%ebp
   0x00000003 <+3>:   sub    $0x10,%esp
   0x00000006 <+6>:   movl   $0x0,-0x4(%ebp)  // i = 0   i = 0
   0x0000000d <+13>:  addl   $0x1,-0x4(%ebp)  // i++     i = 1
   0x00000011 <+17>:  mov    -0x4(%ebp),%eax  // j = i   i = 1  j = 1
   0x00000014 <+20>:  add    %eax,%eax        // j += j  i = 1  j = 2
   0x00000016 <+22>:  add    %eax,-0x4(%ebp)  // i += j  i = 3
   0x00000019 <+25>:  addl   $0x1,-0x4(%ebp)  // i++     i = 4
   0x0000001d <+29>:  leave  
   0x0000001e <+30>:  ret
End of assembler dump.

(मुझे लगता है कि 0x00000014 निर्देश किसी प्रकार का संकलक अनुकूलन था?)


मुझे लगता है कि सी 99 मानक के प्रासंगिक भाग 6.5 अभिव्यक्तियां हैं, §2

पिछले और अगले अनुक्रम बिंदु के बीच एक ऑब्जेक्ट में अभिव्यक्ति के मूल्यांकन द्वारा एक बार अपने संग्रहित मूल्य को संशोधित किया जाएगा। इसके अलावा, पूर्व मान केवल संग्रहीत करने के मूल्य को निर्धारित करने के लिए पढ़ा जाएगा।

और 6.5.16 असाइनमेंट ऑपरेटर, §4:

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


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

तो अनिच्छुक व्यवहार पर आगे बढ़ना, ड्राफ्ट सी 99 मानक खंड 6.5 अनुच्छेद 3 में कहते हैं ( जोर मेरा ):

ऑपरेटरों और ऑपरेटरों का समूह वाक्यविन्यास द्वारा इंगित किया गया है .74) बाद में निर्दिष्ट (फ़ंक्शन-कॉल (), &&, ||,?:, और अल्पविराम ऑपरेटरों के लिए), उप-अभिव्यक्तियों के मूल्यांकन के क्रम और क्रम में कौन सा दुष्प्रभाव होता है, दोनों निर्दिष्ट नहीं हैं।

तो जब हमारे पास इस तरह की रेखा है:

i = i++ + ++i;

हम नहीं जानते कि i++ या ++i मूल्यांकन पहले किया जाएगा। यह मुख्य रूप से अनुकूलन के लिए संकलक बेहतर विकल्प देने के लिए है ।

हमने यहां भी अपरिभाषित व्यवहार भी किया है क्योंकि कार्यक्रम अनुक्रम बिंदुओं के बीच एक से अधिक बार चर ( i , u , आदि ..) को संशोधित कर रहा है। ड्राफ्ट मानक सेक्शन 6.5 अनुच्छेद 2 ( जोर मेरा ):

पिछले और अगले अनुक्रम बिंदु के बीच एक ऑब्जेक्ट में अभिव्यक्ति के मूल्यांकन द्वारा एक बार अपने संग्रहित मूल्य को संशोधित किया जाएगा। इसके अलावा, पूर्व मान केवल संग्रहीत करने के मूल्य को निर्धारित करने के लिए पढ़ा जाएगा

यह निम्न कोड उदाहरणों को अपरिभाषित के रूप में उद्धृत करता है:

i = ++i + 1;
a[i++] = i; 

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

i = i++ + ++i;
^   ^       ^

i = (i++);
^    ^

u = u++ + ++u;
^   ^       ^

u = (u++);
^    ^

v = v++ + ++v;
^   ^       ^

अनिच्छुक व्यवहार को धारा 3.4.4 में ड्राफ्ट सी 99 मानक में परिभाषित किया गया है:

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

और अपरिभाषित व्यवहार को धारा 3.4.3 में परिभाषित किया गया है:

व्यवहार, एक गैर-योग्य या गलत प्रोग्राम निर्माण या गलत डेटा के उपयोग पर, जिसके लिए यह अंतर्राष्ट्रीय मानक कोई आवश्यकता नहीं लगाता है

और नोट करता है कि:

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


सी मानक से उद्धृत अधिकांश उत्तर इस बात पर बल देते हैं कि इन संरचनाओं का व्यवहार अनिर्धारित है। यह समझने के लिए कि इन संरचनाओं का व्यवहार अपरिभाषित क्यों है , चलो इन शर्तों को पहले सी 11 मानक के प्रकाश में समझें:

अनुक्रमित: (5.1.2.3)

किसी भी दो मूल्यांकन A और B को देखते हुए, यदि A को B से पहले अनुक्रमित किया गया है, तो A का निष्पादन B के निष्पादन से पहले होगा।

Unsequenced:

यदि A B पहले या बाद में अनुक्रमित नहीं है, तो A और B का अपूर्ण नहीं है।

मूल्यांकन दो चीजों में से एक हो सकता है:

  • मूल्य गणना , जो एक अभिव्यक्ति के परिणाम का काम करते हैं; तथा
  • दुष्प्रभाव , जो वस्तुओं के संशोधन हैं।

अनुक्रम बिंदु:

अभिव्यक्ति A और B के मूल्यांकन के बीच एक अनुक्रम बिंदु की उपस्थिति का तात्पर्य है कि A के साथ जुड़े प्रत्येक मूल्य गणना और साइड इफेक्ट को प्रत्येक मान गणना और B जुड़े साइड इफेक्ट से पहले अनुक्रमित किया जाता है।

अब अभिव्यक्ति के लिए सवाल पर आ रहा है

int i = 1;
i = i++;

मानक कहता है कि:

6.5 अभिव्यक्तियां:

यदि स्केलर ऑब्जेक्ट पर साइड इफेक्ट एक ही स्केलर ऑब्जेक्ट पर एक अलग दुष्प्रभाव या समान स्केलर ऑब्जेक्ट के मान का उपयोग करके मूल्य गणना के सापेक्ष अपरिचित है , तो व्यवहार अपरिभाषित है । [...]

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

आइए असाइनमेंट के बाईं ओर i नाम दें i और il के दाईं ओर (अभिव्यक्ति i++ ) ir , तो अभिव्यक्ति की तरह हो

il = ir++     // Note that suffix l and r are used for the sake of clarity.
              // Both il and ir represents the same object.  

Postfix ++ ऑपरेटर के बारे में एक महत्वपूर्ण बिंदु यह है कि:

सिर्फ इसलिए कि चर के बाद ++ आता है इसका मतलब यह नहीं है कि वृद्धि देर से होती हैजब तक संकलक सुनिश्चित करता है कि मूल मान का उपयोग किया जाता है तब तक संकलन तब तक हो सकता है जब तक संकलक पसंद करता है

इसका मतलब है कि अभिव्यक्ति il = ir++ का मूल्यांकन या तो किया जा सकता है

temp = ir;      // i = 1
ir = ir + 1;    // i = 2   side effect by ++ before assignment
il = temp;      // i = 1   result is 1  

या

temp = ir;      // i = 1
il = temp;      // i = 1   side effect by assignment before ++
ir = ir + 1;    // i = 2   result is 2  

जिसके परिणामस्वरूप दो अलग-अलग परिणाम 1 और 2 जो असाइनमेंट और ++ द्वारा साइड इफेक्ट्स के अनुक्रम पर निर्भर करता है और इसलिए यूबी को आमंत्रित करता है।


सी में अपरिभाषित व्यवहार की अवधारणा है, यानी कुछ भाषा संरचनाएं सिंटैक्टिक रूप से वैध हैं लेकिन कोड चलाने पर आप व्यवहार की भविष्यवाणी नहीं कर सकते हैं।

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

तो, इस बात को ध्यान में रखते हुए, ये "मुद्दे" क्यों हैं? भाषा स्पष्ट रूप से कहती है कि कुछ चीजें अपरिभाषित व्यवहार की ओर ले जाती हैं । कोई समस्या नहीं है, इसमें कोई "चाहिए" शामिल नहीं है। यदि अपरिभाषित व्यवहार तब बदलता है जब शामिल चरों में से एक volatile घोषित किया जाता है, जो कुछ भी साबित या परिवर्तित नहीं करता है। यह अपरिभाषित है ; आप व्यवहार के बारे में कारण नहीं कर सकते हैं।

आपका सबसे दिलचस्प दिखने वाला उदाहरण, वाला एक

u = (u++);

अपरिभाषित व्यवहार का एक टेक्स्ट-बुक उदाहरण है ( अनुक्रम बिंदुओं पर विकिपीडिया की प्रविष्टि देखें)।


https://.com/questions/29505280/incrementing-array-index-in-c किसी ने एक बयान के बारे में पूछा जैसे:

int k[] = {0,1,2,3,4,5,6,7,8,9,10};
int i = 0;
int num;
num = k[++i+k[++i]] + k[++i];
printf("%d", num);

जो 7 प्रिंट करता है ... ओपी ने इसे प्रिंट करने की उम्मीद की 6।

++i increments की गणना बाकी गणनाओं से पहले पूरी होने की गारंटी नहीं है। वास्तव में, विभिन्न कंपाइलरों को यहां अलग-अलग परिणाम मिलेंगे। आपके द्वारा प्रदान किए गए उदाहरण में, पहले 2 ++i निष्पादित किया, तो के k[] के मान पढ़े गए, फिर अंतिम ++i फिर k[]

num = k[i+1]+k[i+2] + k[i+3];
i += 3

आधुनिक कंपाइलर्स इसे बहुत अच्छी तरह अनुकूलित करेंगे। असल में, आपके द्वारा मूल रूप से लिखे गए कोड से संभवतः बेहतर (माना जाता है कि जिस तरह से आपने आशा की थी)।





sequence-points