सी में सरणी सूचकांकों (बनाम अभिव्यक्ति) के मूल्यांकन का क्रम


47

इस कोड को देखकर:

static int global_var = 0;

int update_three(int val)
{
    global_var = val;
    return 3;
}

int main()
{
    int arr[5];
    arr[global_var] = update_three(2);
}

कौन सी सरणी प्रविष्टि अपडेट की जाती है? 0 या 2?

क्या सी के विनिर्देश में एक हिस्सा है जो इस विशेष मामले में ऑपरेशन की पूर्वता को इंगित करता है?


21
इससे अपरिभाषित व्यवहार की बू आती है। यह निश्चित रूप से कुछ ऐसा है जिसे जानबूझकर कोड नहीं किया जाना चाहिए।
फिडलिंग बिट्स

1
मैं मानता हूं कि यह खराब कोडिंग का उदाहरण है।
Jiminion

4
कुछ उपाख्यान परिणाम: godbolt.org/z/hM2Jo2
Bob__

15
इसका सरणी सूचकांकों या संचालन के आदेश से कोई लेना-देना नहीं है। इसका क्या करना है सी कल्पना "सीक्वेंस पॉइंट्स" को बुलाती है, और विशेष रूप से, यह तथ्य कि असाइनमेंट एक्सप्रेशंस लेफ्ट-हैंड और राइट-हैंड एक्सप्रेशन के बीच सीक्वेंस पॉइंट नहीं बनाते हैं, इसलिए कंपाइलर ऐसा करने के लिए स्वतंत्र है। चुनता है।
ली डेनियल क्रोकर

4
आपको एक सुविधा अनुरोध की रिपोर्ट करनी चाहिए clangताकि कोड का यह टुकड़ा एक चेतावनी आईएमएचओ को ट्रिगर करे।
मलत

जवाबों:


51

वाम और अधिकार संचालन का आदेश

में असाइनमेंट करने के लिए arr[global_var] = update_three(2), सी कार्यान्वयन को ऑपरेंड का मूल्यांकन करना चाहिए और, साइड इफेक्ट के रूप में, बाएं ऑपरेंड के संग्रहीत मूल्य को अपडेट करें। सी 2018 6.5.16 (जो असाइनमेंट के बारे में है) पैरा 3 बताता है कि बाएं और दाएं ऑपरेंड में कोई अनुक्रमण नहीं है:

ऑपरेंड का मूल्यांकन अप्राप्त है।

इसका मतलब यह है कि सी कार्यान्वयन पहले लैवल्यू की गणना करने के लिए स्वतंत्र है arr[global_var]( लैवल्यू की गणना करके, हमारा मतलब है कि यह अभिव्यक्ति क्या कहती है), फिर मूल्यांकन करने के लिए update_three(2), और अंत में पूर्व के उत्तरार्द्ध के मूल्य को असाइन करने के लिए; या update_three(2)पहले मूल्यांकन करने के लिए , फिर अंतराल की गणना करें, फिर पूर्व को बाद में असाइन करें; या लैवल्यू का मूल्यांकन करने के लिए और update_three(2)कुछ रुक-रुक कर फैशन में और फिर बाईं लैवल्यू पर सही मान असाइन करें।

सभी मामलों में, लैवल्यू को मान का असाइनमेंट अंतिम आना चाहिए, क्योंकि 6.5.16 3 भी कहता है:

… बाएं ऑपरेंड के संग्रहीत मूल्य को अपडेट करने का साइड इफेक्ट बाएं और दाएं ऑपरेंड के मूल्य गणनाओं के बाद अनुक्रमित होता है…

अनुक्रमण उल्लंघन

कुछ का उपयोग करने के लिए अपरिभाषित व्यवहार के बारे में विचार करना चाहिए global_varऔर 6.5 2 के उल्लंघन में इसे अलग से अपडेट करना चाहिए, जो कहता है:

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

यह कई सी चिकित्सकों के लिए काफी परिचित है कि अभिव्यक्ति का व्यवहार जैसे x + x++कि सी मानक द्वारा परिभाषित नहीं है, क्योंकि वे दोनों के मूल्य का उपयोग करते हैं xऔर अलग-अलग इसे एक ही अभिव्यक्ति में बिना अनुक्रमण के संशोधित करते हैं। हालांकि, इस मामले में, हमारे पास एक फ़ंक्शन कॉल है, जो कुछ अनुक्रमण प्रदान करता है। global_varमें उपयोग किया जाता है arr[global_var]और फ़ंक्शन कॉल में अपडेट किया जाता है update_three(2)

6.5.2.2 10 हमें बताता है कि फंक्शन कहे जाने से पहले एक अनुक्रम बिंदु है:

फ़ंक्शन डिज़ाइनर और वास्तविक तर्कों के मूल्यांकन के बाद एक अनुक्रम बिंदु है, लेकिन वास्तविक कॉल से पहले ...

समारोह के अंदर, global_var = val;एक है पूर्ण अभिव्यक्ति है, और इसलिए है 3में return 3;, प्रति 6.8 4:

एक पूर्ण अभिव्यक्ति एक अभिव्यक्ति है जो अन्य अभिव्यक्ति का हिस्सा नहीं है, न ही एक घोषणाकर्ता या सार घोषणाकर्ता का हिस्सा ...

फिर इन दो अभिव्यक्तियों के बीच एक अनुक्रम बिंदु है, फिर से प्रति 6.8 4:

… एक पूर्ण अभिव्यक्ति के मूल्यांकन और अगली पूर्ण अभिव्यक्ति के मूल्यांकन के बीच अनुक्रम बिंदु का मूल्यांकन किया जाना है।

इस प्रकार, सी कार्यान्वयन arr[global_var]पहले मूल्यांकन कर सकता है और फिर फ़ंक्शन कॉल कर सकता है, उस स्थिति में उनके बीच एक अनुक्रम बिंदु है क्योंकि फ़ंक्शन कॉल से पहले एक है, या यह global_var = val;फ़ंक्शन कॉल में मूल्यांकन कर सकता है और फिर arr[global_var], किस मामले में है उनके बीच एक अनुक्रम बिंदु क्योंकि पूर्ण अभिव्यक्ति के बाद एक है। तो व्यवहार अनिर्दिष्ट है - या तो उन दो चीजों का पहले मूल्यांकन किया जा सकता है - लेकिन यह अपरिभाषित नहीं है।


24

यहां का परिणाम अनिर्दिष्ट है

जबकि एक अभिव्यक्ति में संचालन का क्रम, जो निर्धारित करता है कि कैसे सबएक्सप्रेस को समूहीकृत किया जाता है, अच्छी तरह से परिभाषित किया गया है, मूल्यांकन का क्रम निर्दिष्ट नहीं है। इस मामले में इसका मतलब है कि या तो global_varपहले पढ़ा जा सकता है या कॉल update_threeपहले हो सकता है, लेकिन यह जानने का कोई तरीका नहीं है।

यहां अपरिभाषित व्यवहार नहीं है क्योंकि एक फ़ंक्शन कॉल एक अनुक्रम बिंदु का परिचय देता है, जैसा कि फ़ंक्शन में प्रत्येक कथन जिसमें संशोधन होता है global_var

स्पष्ट करने के लिए, C मानक 3.4.3 में अपरिभाषित व्यवहार को परिभाषित करता है :

अपरिभाषित व्यवहार

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

और धारा 3.4.4 में अनिर्दिष्ट व्यवहार को परिभाषित करता है :

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

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

मानक कहता है कि फ़ंक्शन तर्कों का मूल्यांकन क्रम अनिर्दिष्ट है, जो इस मामले में इसका मतलब है कि या तो arr[0]3 पर arr[2]सेट हो जाता है या 3 पर सेट हो जाता है।


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

2
@ EricPostpischil पूर्व C11 शब्दावली में एक फ़ंक्शन के प्रवेश और निकास पर एक अनुक्रम बिंदु है। C11 शब्दावली में पूरे फ़ंक्शन बॉडी को कॉलिंग संदर्भ के संबंध में अनिश्चित रूप से अनुक्रमित किया जाता है। ये दोनों एक ही चीज़ को निर्दिष्ट कर रहे हैं, बस अलग-अलग शब्दों का उपयोग करते हुए
एमएम

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

1
@kuroineko सिर्फ इसलिए कि उत्पादन अलग-अलग हो सकता है यह स्वचालित रूप से अपरिभाषित व्यवहार नहीं करता है। अपरिभाषित बनाम अनिर्दिष्ट व्यवहार के लिए मानक की अलग-अलग परिभाषाएं हैं, और इस स्थिति में यह बाद की बात है।
dbush

@EricPostpischil आपके यहां अनुक्रम बिंदु हैं (C11 सूचनात्मक अनुलग्नक C से): "फ़ंक्शन कॉल में वास्तविक डिज़ाइनर और वास्तविक तर्क के मूल्यांकन के बीच और वास्तविक कॉल। (6.5.2.2)", "पूर्ण अभिव्यक्ति के मूल्यांकन के बीच। और अगले पूर्ण अभिव्यक्ति का मूल्यांकन किया जाना है ... / - / ... (वैकल्पिक) एक वापसी बयान में अभिव्यक्ति (6.8.6.4) "। और अच्छी तरह से, प्रत्येक अर्धविराम पर भी, क्योंकि यह एक पूर्ण अभिव्यक्ति है।
लुंडिन

1

मैंने कोशिश की और मुझे प्रविष्टि 0 अपडेट मिली।

हालांकि इस प्रश्न के अनुसार: हमेशा पहले मूल्यांकन किया गया एक अभिव्यक्ति का दाहिना हाथ होगा

मूल्यांकन का क्रम अनिर्दिष्ट और निष्कासित है। इसलिए मुझे लगता है कि इस तरह के एक कोड से बचना चाहिए।


मुझे प्रवेश 0 पर भी अपडेट मिला है।
Jiminion

1
व्यवहार अपरिभाषित नहीं है लेकिन अनिर्दिष्ट है। स्वाभाविक रूप से या तो इस पर निर्भर होने से बचना चाहिए।
अंती हापला

@AnttiHaapala मैंने संपादित किया है
बी।

1
हम्म आह और यह अप्रयुक्त नहीं है, लेकिन अनिश्चितकालीन अनुक्रम है ... एक कतार में बेतरतीब ढंग से खड़े 2 लोग अनिश्चित काल के अनुक्रम हैं। एजेंट स्मिथ के अंदर नियो बिना आरोपित हैं और अपरिभाषित व्यवहार होगा।
अंती हापला

0

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

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

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

int idx = global_var;
arr[idx] = update_three(2);

या कोडिंग:

int temp = update_three(2);
arr[global_var] = temp;

अंगूठे के एक अच्छे नियम के रूप में: जब तक वैश्विक वैरिएबल नहीं होते हैं const(या वे नहीं हैं लेकिन आप जानते हैं कि कोई भी कोड उन्हें कभी भी साइड इफेक्ट के रूप में नहीं बदलेगा), तो आपको उन्हें सीधे कोड में उपयोग नहीं करना चाहिए, जैसे कि बहु-थ्रेडेड वातावरण में, यहां तक ​​कि यह अपरिभाषित हो सकता है:

int result = global_var + (2 * global_var);
// Is not guaranteed to be equal to `3 * global_var`!

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

(बस मामले में कोई पूछता है क्यों किसी को भी करना होगा x + 2 * xबजाय 3 * x- पर कुछ सीपीयू अलावा अल्ट्रा तेज है और इसलिए एक शक्ति दो से गुणा है के रूप में संकलक (बिट पाली में इन बदल जाएगी 2 * x == x << 1), फिर भी मनमाने ढंग से संख्या के साथ गुणा बहुत धीमी गति से किया जा सकता है , इस प्रकार 3 से गुणा करने के बजाय, आप 1 के द्वारा x को थोड़ा सा शिफ्ट करके और परिणाम में x को जोड़कर बहुत तेज कोड प्राप्त करते हैं - और यहां तक ​​कि आधुनिक कंपाइलरों द्वारा चाल भी किया जाता है यदि आप 3 से गुणा करते हैं और आक्रामक अनुकूलन चालू करते हैं जब तक कि यह एक आधुनिक लक्ष्य न हो। सीपीयू जहां गुणा कई गुना तेज है, तब से चाल धीमी हो जाएगी।)


2
यह अपरिभाषित व्यवहार नहीं है - मानक सूची संभावनाओं और उनमें से किसी एक को किसी भी उदाहरण पर चुना जाता है
एंटी हापला 1

संकलक 3 * xx के दो रीड में नहीं बदलेगा । यह x को एक बार पढ़ सकता है और फिर x + 2 * x विधि को उस रजिस्टर पर कर सकता है जिसे x में पढ़ा गया है
MM

6
@Mecki "यदि आप यह नहीं कह सकते कि परिणाम केवल कोड को देखकर क्या है, तो परिणाम अपरिभाषित है" - अपरिभाषित व्यवहार का C / C ++ में बहुत विशिष्ट अर्थ है, और यह नहीं है। अन्य उत्तरदाताओं ने समझाया है कि यह विशेष उदाहरण अनिर्दिष्ट क्यों है , लेकिन अपरिभाषित नहीं है ।
मार्सेलम

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

2
@ मेकी " अनिर्धारित का अंग्रेजी भाषा में एक बहुत ही खास अर्थ है " ... लेकिन एक प्रश्न के लेबल में language-lawyer, जहां विचाराधीन भाषा का अपरिभाषित के लिए अपना "बहुत विशेष अर्थ" है , आप केवल उपयोग न करके भ्रम पैदा करने वाले हैं भाषा की परिभाषा।
ट्रिपहाउंड

-1

ग्लोबल एडिट: सॉरी दोस्तों, मैंने सभी को निकाल दिया और बहुत सारी बकवास लिखी। बस एक बूढ़ा गीजर शेख़ी मार रहा है।

मैं विश्वास करना चाहता था कि सी को बख्श दिया गया था, लेकिन अफसोस कि C11 के बाद से इसे C ++ के बराबर लाया गया है। जाहिर है, यह जानते हुए कि संकलक अभिव्यक्ति में साइड इफेक्ट्स के साथ क्या करेंगे, अब थोड़ा मैथ्स रिडल को हल करने के लिए कोड अनुक्रमों के आंशिक क्रम को शामिल करने की आवश्यकता होती है, जो "एक पर आधारित है" के सिंक्रनाइज़ेशन बिंदु से पहले स्थित है।

मुझे लगता है कि K & R दिनों (इलेक्ट्रिक कार के नियंत्रक सहित) में कुछ महत्वपूर्ण वास्तविक-समय के एम्बेडेड सिस्टम को डिजाइन और कार्यान्वित किया गया है जो इंजन को जांच में नहीं रखे जाने पर निकटतम दीवार में दुर्घटनाग्रस्त लोगों को भेज सकते हैं, एक 10 टन औद्योगिक रोबोट जो लोगों को एक लुगदी के लिए स्क्वाश कर सकता है यदि ठीक से आज्ञा नहीं दी जाती है, और एक प्रणाली परत जो हानिरहित है, तो कुछ दर्जन प्रोसेसर होंगे जो 1% से कम सिस्टम ओवरहेड के साथ अपने डेटा बस को सूखा लेते हैं)।

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

आंखों के पानी के मेमोरी बैरियर एब्स्ट्रक्शन के यह सभी ट्रक बस मल्टी-सीपीयू कैश सिस्टम की सीमाओं के एक अस्थायी सेट के कारण हैं, जिनमें से सभी को सामान्य ओएस सिंक्रनाइज़ेशन ऑब्जेक्ट्स में सुरक्षित रूप से समझाया जा सकता है, जैसे, म्यूटेक्स और स्थिति चर C ++। प्रदान करता है।
इस एनकैप्सुलेशन की लागत है, लेकिन प्रदर्शन में एक मिनट की गिरावट के साथ तुलना की जाती है कि विशिष्ट दाने वाले विशिष्ट सीपीयू निर्देशों का उपयोग कुछ मामलों में क्या कर सकता है। कीवर्ड (या एक
volatile#pragma dont-mess-with-that-variableसभी के लिए, एक सिस्टम प्रोग्रामर के रूप में, देखभाल) कंपाइलर को मेमोरी एक्सेस को फिर से रोकने के लिए बताने के लिए काफी होता। इष्टतम कोड आसानी से प्रत्यक्ष asm निर्देशों के साथ निम्न स्तर के ड्राइवर और OS कोड को तदर्थ CPU विशिष्ट निर्देशों के साथ छिड़का जा सकता है। अंतर्निहित हार्डवेयर (कैश सिस्टम या बस इंटरफ़ेस) कैसे काम करता है, इसका अंतरंग ज्ञान के बिना, आप वैसे भी बेकार, अक्षम या दोषपूर्ण कोड लिखने के लिए बाध्य हैं।

volatileकीवर्ड और बॉब का एक मिनट का समायोजन हर किसी का होता लेकिन सबसे कम स्तर के प्रोग्रामर चाचा का। इसके बजाय, C ++ मैथ्स के सामान्य गिरोह के लोगों के पास एक फील्ड डे डिजाइनिंग थी जो अब तक एक और समझ से बाहर है, गैर-मौजूद समस्याओं की तलाश करने के लिए अपनी विशिष्ट प्रवृत्ति के लिए तैयार है और एक संकलक के चश्मे के साथ एक प्रोग्रामिंग भाषा की परिभाषा को गलत करता है।

केवल इस बार सी के एक मूलभूत पहलू को खराब करने के लिए आवश्यक परिवर्तन, क्योंकि इन "बाधाओं" को निम्न स्तर के सी कोड में भी ठीक से काम करने के लिए उत्पन्न किया जाना था। अन्य बातों के अलावा, अभिव्यक्ति की परिभाषा में कोई व्याख्या या औचित्य नहीं है।

निष्कर्ष के रूप में, तथ्य यह है कि एक कंपाइलर सी के इस बेतुके टुकड़े से एक सुसंगत मशीन कोड का उत्पादन कर सकता है जिस तरह से सी ++ लोग 2000 के दशक के अंत में कैश सिस्टम की संभावित विसंगतियों के साथ मुकाबला करते हैं।
इसने C के एक मूलभूत पहलू (अभिव्यक्ति परिभाषा) का एक भयानक गड़बड़ कर दिया, ताकि C प्रोग्रामर का विशाल बहुमत - जो कैश सिस्टम के बारे में कोई लानत नहीं देते हैं, और ठीक ही तो - अब समझाने के लिए गुरुओं पर भरोसा करने को मजबूर हैं a = b() + c()और के बीच अंतर a = b + c

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

मैं इस अपरिभाषित / अनिर्दिष्ट बहस के महत्व को समझने में भी विफल हूं। या तो आप संगत व्यवहार के साथ कोड उत्पन्न करने के लिए संकलक पर भरोसा कर सकते हैं या आप नहीं कर सकते। चाहे आप उस अपरिभाषित या अनिर्दिष्ट को म्यूट बिंदु की तरह कहते हैं।

मेरे स्पष्ट रूप से सूचित राय में, सी पहले से ही अपने K & R राज्य में काफी खतरनाक है। एक उपयोगी विकास सामान्य ज्ञान सुरक्षा उपायों को जोड़ना होगा। उदाहरण के लिए, इस उन्नत कोड विश्लेषण उपकरण का उपयोग करने से ऐनक कंपाइलर को कम से कम बोनट कोड के बारे में चेतावनी देने के लिए लागू करने के लिए मजबूर करता है, बजाय चुपचाप कोड के चरम से अविश्वसनीय रूप से उत्पन्न करने के लिए।
लेकिन इसके बजाय, लोगों ने उदाहरण के लिए, C ++ 17 में एक निश्चित मूल्यांकन आदेश को परिभाषित करने का निर्णय लिया। अब हर सॉफ्टवेयर इम्बेकाइल को सक्रिय रूप से उसके / उसके कोड में साइड इफेक्ट डालने के लिए उकसाया जाता है, यह सुनिश्चित करते हुए कि नए कंपाइलर निर्धारित रूप से ओफ्यूजेशन को उत्सुकता से संभालेंगे।

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

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


यह अनिर्धारित व्यवहार है यदि एक ही वस्तु में समान प्रभाव, C17 6.5 / 2 पर दुष्प्रभाव होते हैं। इन्हें C17 6.5.18 / 3 के अनुसार आरोपित किया गया है। लेकिन 6.5 / 2 से पाठ "यदि एक स्केलर ऑब्जेक्ट पर एक साइड इफेक्ट एक समान स्केलर ऑब्जेक्ट पर एक अलग साइड इफेक्ट या एक ही स्केलर ऑब्जेक्ट के मूल्य का उपयोग करते हुए एक मूल्य संगणना के सापेक्ष अनुपलब्ध है, तो व्यवहार अपरिभाषित है।" लागू नहीं होता है, क्योंकि फ़ंक्शन इंडेक्स के अंदर वैल्यू कंप्यूटेशन एरे इंडेक्स एक्सेस से पहले या बाद में सेव किया जाता है, असाइनमेंट ऑपरेटर की परवाह किए बिना अपने आप में अप्रयुक्त ऑपरेंड होने के बावजूद।
लुंडिन

यदि आप चाहें तो फंक्शन कॉल "म्यूटेक्स के खिलाफ एक म्यूटेक्स" की तरह कार्य करता है। अस्पष्ट कॉमा ऑपरेटर बकवास की तरह 0,expr,0
लुंडिन

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

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

मैं आपकी टिप्पणी में इस बिंदु को देखने में विफल हूं। संकलक-विशिष्ट व्यवहार पर भरोसा करना गैर-पोर्टेबिलिटी की गारंटी है। इसके लिए संकलक निर्माता में बहुत विश्वास की आवश्यकता होती है, जो किसी भी समय इन "अतिरिक्त परिभाषाओं" में से किसी को भी बंद कर सकता है। केवल एक कंपाइलर कर सकता है चेतावनी उत्पन्न करता है, जिसे एक बुद्धिमान और जानकार प्रोग्रामर त्रुटियों की तरह संभालने का फैसला कर सकता है। इस आईएसओ मॉन्स्टर के साथ जो समस्या मुझे दिखाई देती है वह यह है कि यह ओपी के उदाहरण को वैध (बेहद स्पष्ट कारणों के लिए, एक अभिव्यक्ति के K & R परिभाषा के साथ तुलना में) इस तरह के अत्याचारपूर्ण कोड बनाता है।
कुरोई नेको
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.