अपरिभाषित व्यवहार के पीछे दर्शन


59

C \ C ++ विनिर्देशों को कंपाइलरों के अपने तरीके से लागू करने के लिए बड़ी संख्या में खुले व्यवहार को छोड़ दिया जाता है। ऐसे कई सवाल हैं जो हमेशा उसी के बारे में पूछे जाते रहते हैं और हमारे पास इसके बारे में कुछ बेहतरीन पोस्ट हैं:

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

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

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

यदि आप प्रश्न को नहीं समझते हैं या आपके उत्तर को वापस लेने के लिए पर्याप्त प्रमाण / स्रोत नहीं हैं, तो कृपया मोटे तौर पर अटकलें वाले उत्तर पोस्ट न करें।


7
जो कभी भी एक निर्णायक कंप्यूटर के बारे में सुना है?
सोवा

1
जैसा कि लिटब का उत्कृष्ट उत्तर प्रोग्रामर .stackexchange.com/a/99741/192238 इंगित करता है, इस सवाल का शीर्षक और निकाय थोड़ा बेमेल लगता है: "कंपाइलरों द्वारा अपने तरीके से लागू करने के लिए खुला व्यवहार" आमतौर पर कार्यान्वयन-परिभाषित के रूप में संदर्भित किया जाता है । निश्चित रूप से, वास्तविक यूबी को कार्यान्वयन लेखक द्वारा परिभाषित करने की अनुमति है, लेकिन अधिक बार नहीं, वे परेशान नहीं करते हैं (और इसे सभी को दूर करते हैं, आदि)
अंडरस्कोर_ड

जवाबों:


49

सबसे पहले, मैं ध्यान दूंगा कि यद्यपि मैं यहाँ केवल "C" का उल्लेख करता हूं, वही वास्तव में C ++ के बारे में भी समान रूप से लागू होता है।

गोडेल का उल्लेख करने वाली टिप्पणी आंशिक रूप से (लेकिन केवल आंशिक रूप से) बिंदु पर थी।

जब आप इसे करने के लिए नीचे उतरते हैं, तो सी मानकों में अपरिभाषित व्यवहार काफी हद तक केवल मानक प्रयासों को परिभाषित करने के बीच की सीमा को इंगित करता है, और यह क्या नहीं करता है।

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

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

अहंकार के आरोप में (अभी तक फिर से) जोखिम में, मैं सी मानक को दो बुनियादी विचारों द्वारा शासित (भाग में) होने के रूप में चिह्नित करूंगा:

  1. भाषा को यथासंभव विभिन्न प्रकार के हार्डवेयर का समर्थन करना चाहिए (आदर्श रूप से, सभी "समझदार" हार्डवेयर कुछ उचित कम सीमा तक)।
  2. दिए गए वातावरण के लिए भाषा को यथासंभव विविध प्रकार के सॉफ्टवेयर लिखने का समर्थन करना चाहिए।

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

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

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

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

एक और प्रकार का अपरिभाषित व्यवहार है (जो C की तुलना में C ++ में बहुत अधिक सामान्य है), जो बस संकलक तकनीक की सीमाओं के कारण मौजूद है - ऐसी चीजें जिन्हें हम मूल रूप से जानते हैं वे त्रुटियां हैं, और संभवतः संकलक को त्रुटियों के रूप में निदान करना पसंद करेंगे, आदि लेकिन संकलक तकनीक पर वर्तमान सीमा को देखते हुए, यह संदिग्ध है कि उन्हें सभी परिस्थितियों में निदान किया जा सकता है। इनमें से कई अन्य आवश्यकताओं से प्रेरित हैं, जैसे कि अलग संकलन के लिए, इसलिए यह काफी हद तक परस्पर विरोधी आवश्यकताओं को संतुलित करने का मामला है, इस मामले में समिति ने आमतौर पर अधिक क्षमताओं का समर्थन करने का विकल्प चुना है, भले ही इसका मतलब है कि कुछ संभावित समस्याओं का निदान करने में कमी, यह सुनिश्चित करने की क्षमताओं को सीमित करने के बजाय कि सभी संभावित समस्याओं का निदान किया जाता है।

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

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


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

9
क्षमा करें, लेकिन मुझे डर है कि आपने गोडेल के सिद्धांतों को पूरी तरह गलत समझा है। वे तर्क की एक सुसंगत प्रणाली में सभी सच्चे बयानों को साबित करने की असंभवता से निपटते हैं; कंप्यूटिंग के संदर्भ में, अपूर्णता प्रमेय यह कहने के लिए अनुरूप है कि ऐसी समस्याएं हैं जो किसी भी कार्यक्रम द्वारा हल नहीं की जा सकती हैं - समस्याएं सच्चे बयानों, साक्ष्यों के कार्यक्रमों और तर्क प्रणाली के लिए गणना के मॉडल के अनुरूप हैं। इसका अपरिभाषित व्यवहार से कोई संबंध नहीं है। यहाँ सादृश्य की व्याख्या के लिए देखें: scottaaronson.com/blog/?p=710
एलेक्स दस ब्रिंक

5
मुझे ध्यान देना चाहिए कि सी कार्यान्वयन के लिए वॉन न्यूमैन मशीन की आवश्यकता नहीं है। यह (और मैं एम्बेडेड सिस्टम पर इस तरह के कार्यान्वयन का एक बहुत देखने के लिए आश्चर्य नहीं होगा) एक हार्वर्ड वास्तुकला के लिए एक सी कार्यान्वयन विकसित करने के लिए पूरी तरह से संभव है (और नहीं भी बहुत मुश्किल) है
bdonlan

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

20

सी औचित्य बताते हैं

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

अनिर्दिष्ट व्यवहार कार्यान्वयनकर्ता को अनुवाद करने वाले कार्यक्रमों में कुछ अक्षांश देता है। यह अक्षांश कार्यक्रम का अनुवाद करने में विफल रहने के रूप में आगे नहीं बढ़ता है।

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

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

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

सी कोड गैर-पोर्टेबल हो सकता है। यद्यपि यह प्रोग्रामर को सही मायने में पोर्टेबल प्रोग्राम लिखने का अवसर देने के लिए प्रयासरत है, कमेटी प्रोग्रामर्स को पोर्ट्रेट रूप से लिखने के लिए मजबूर नहीं करना चाहती थी, सी के उपयोग को एक 'उच्च-स्तरीय असेंबलर' के रूप में करने के लिए: मशीन-विशिष्ट लिखने की क्षमता कोड सी की ताकत में से एक है। यह सिद्धांत है जो काफी हद तक सख्ती से अनुरूप कार्यक्रम और अनुरूप कार्यक्रम (program1.7) के बीच अंतर को आकर्षित करने के लिए प्रेरित करता है ।

और 1.7 पर यह नोट करता है

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

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

इस प्रकार, यह थोड़ा गंदा कार्यक्रम जो जीसीसी पर पूरी तरह से ठीक काम करता है, अभी भी अनुरूप है !


15

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

अन्य अपरिभाषित व्यवहार सिर्फ वास्तविकता को दर्शाता है। एक उदाहरण बिट-शिफ्टिंग है जो गिनती से बड़ा है। यह वास्तव में एक ही परिवार की हार्डवेयर पीढ़ियों के बीच भिन्न है। यदि आपके पास 16-बिट ऐप है, तो वही बाइनरी 80286 और 80386 पर अलग-अलग परिणाम देगा। इसलिए भाषा मानक कहता है कि हम नहीं जानते हैं!

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


दूसरे पैराग्राफ के लिए +1, जो कुछ ऐसा दिखाता है जो कार्यान्वयन-परिभाषित व्यवहार के रूप में निर्दिष्ट होना अजीब होगा।
डेविड थॉर्नले

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

7

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

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

मेरा मानना ​​है कि यह संकलक-लेखक के काम को आसान बनाता है लेकिन मैं अभी उदाहरण को याद नहीं कर सकता। यदि मुझे स्थिति याद आती है तो मैं इसे जोड़ूंगा।


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

6

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


1
है ना? एम्बेडेड हार्डवेयर के साथ अपवादों का क्या करना है?
मेसन व्हीलर 16

2
अपवाद सिस्टम को उन तरीकों से लॉक कर सकते हैं जो एंबेडेड सिस्टम के लिए बहुत खराब हैं जिन्हें जल्दी से जवाब देने की आवश्यकता है। ऐसी स्थितियाँ हैं जहाँ एक झूठा पढ़ना बहुत कम नुकसानदेह होता है जो एक धीमा सिस्टम है।
वर्ल्ड इंजीनियर

1
@ मेसन: क्योंकि हार्डवेयर को अमान्य पहुंच को पकड़ना है। विंडोज के लिए एक एक्सेस उल्लंघन फेंकना आसान है, और एम्बेडेड हार्डवेयर के लिए कठिन है, जिसमें ऑपरेटिंग सिस्टम के अलावा कुछ भी नहीं करना है।
16:16 बजे डेडएमजी

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

4

C का अविष्कार 9bit बाइट्स वाली मशीन पर किया गया था और फ्लोटिंग पॉइंट यूनिट नहीं थी - मान लीजिए कि यह बाइट 9बिट्स, 18 बिट्स की थी और यह कि प्री IEEE754 आर्किटमैटिक का उपयोग करके फ्लोट्स को लागू किया जाना चाहिए?


5
मुझे संदेह है कि आप यूनिक्स के बारे में सोच रहे हैं - सी मूल रूप से पीडीपी -11 पर इस्तेमाल किया गया था, जो वास्तव में बहुत पारंपरिक वर्तमान मानक थे। मुझे लगता है कि बुनियादी विचार फिर भी खड़ा है।
जेरी कॉफिन

@ जेरी - हाँ, तुम सही हो - मैं बूढ़ा हो रहा हूँ!
मार्टिन बेकेट

हाँ - हम में से सबसे अच्छा होता है, मुझे डर है।
जेरी कॉफिन

4

मुझे नहीं लगता कि यूबी के लिए पहला औचित्य कमरे को कंपाइलर को ऑप्टिमाइज़ करने के लिए दिया गया था, लेकिन सिर्फ एक समय में लक्ष्यों के लिए स्पष्ट कार्यान्वयन का उपयोग करने की संभावना थी जब आर्किटेक्चर के पास अब से अधिक विविधता थी (याद रखें कि सी एक पर डिज़ाइन किया गया था PDP-11, जो कुछ हद तक परिचित वास्तुकला है, पहला पोर्ट हनीवेल 635 था जो अब तक कम परिचित है - पता योग्य शब्द, 36 बिट शब्दों, 6 या 9 बिट्स बाइट्स, 18 बिट पते का उपयोग करके ... अच्छी तरह से कम से कम यह 2 का उपयोग करता है पूरक हैं)। लेकिन अगर भारी अनुकूलन एक लक्ष्य नहीं था, तो स्पष्ट कार्यान्वयन में अतिप्रवाह के लिए रन-टाइम चेक जोड़ना शामिल नहीं है, रजिस्टर आकार पर बदलाव की गणना करता है, जो कि कई मूल्यों को संशोधित करने वाले अभिव्यक्ति में उपनाम है।

खाते में ली गई एक और चीज़ कार्यान्वयन में आसानी थी। उस समय एसी कंपाइलर कई प्रक्रियाओं का उपयोग करते हुए कई पास था क्योंकि एक प्रक्रिया को संभालने से सब कुछ संभव नहीं होगा (प्रोग्राम बहुत बड़ा हो गया होगा)। भारी सामंजस्य की जाँच से पूछना रास्ते से बाहर था - खासकर जब इसमें कई सीयू शामिल थे। (सी कंपाइलर, लिंट की तुलना में एक और कार्यक्रम, इसके लिए इस्तेमाल किया गया था)।


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

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

1
जो मुझे उत्सुक लगता है वह कथन है जो मैंने "सी मान लिया है कि प्रोग्रामर कभी अपरिभाषित व्यवहार में संलग्न नहीं होंगे" - एक ऐसा तथ्य जो ऐतिहासिक रूप से सच नहीं है। एक सही कथन "सी" माना जाता था कि प्रोग्रामर मानक द्वारा अपरिभाषित व्यवहार को ट्रिगर नहीं करेंगे, जब तक कि उस व्यवहार के प्राकृतिक प्लेटफ़ॉर्म परिणामों से निपटने के लिए तैयार नहीं किया जाता । यह देखते हुए कि सी को सिस्टम-प्रोग्रामिंग भाषा के रूप में डिज़ाइन किया गया था, इसके उद्देश्य का एक बड़ा हिस्सा। प्रोग्रामर को सिस्टम-विशिष्ट चीजों को भाषा मानक द्वारा परिभाषित नहीं करने की अनुमति देने के लिए, यह विचार कि वे ऐसा कभी नहीं करेंगे बेतुका है।
Supercat

प्रोग्रामर्स के लिए अतिरिक्त प्रयासों से गुजरना अच्छा होता है ताकि उन मामलों में पोर्टेबिलिटी सुनिश्चित की जा सके, जहां अलग-अलग प्लेटफॉर्म अलग-अलग काम करेंगे , लेकिन कंपाइलर राइटर हर किसी का समय बर्बाद करते हैं , जब वे उन व्यवहारों को खत्म कर देते हैं, जो प्रोग्रामर ऐतिहासिक रूप से सभी भविष्य के कंपाइलरों से आम तौर पर होने की उम्मीद कर सकते हैं। पूर्णांक iऔर n, इस तरह के n < INT_BITSऔर i*(1<<n)अतिप्रवाह नहीं होने को देखते हुए , मैं इससे i<<=n;अधिक स्पष्ट होगा i=(unsigned)i << n;; कई प्लेटफार्मों पर यह की तुलना में तेज और छोटा होगा i*=(1<<N);। क्या संकलक इसे प्राप्त करने से मना कर दिया है?
सुपरकैट

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

3

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


पूर्णांक जोड़ एक दिलचस्प मामला है; ट्रैप व्यवहार की संभावना से परे जो कुछ मामलों में उपयोगी होगा, लेकिन अन्य मामलों में यादृच्छिक कोड निष्पादन का कारण बन सकता है, ऐसी परिस्थितियां हैं जहां संकलक के लिए उचित होगा कि इस तथ्य के आधार पर निष्कर्ष निकालना कि पूर्णांक लपेटना निर्दिष्ट नहीं है। उदाहरण के लिए, एक कंपाइलर जहां int16 बिट्स और साइन-विस्तारित शिफ्ट्स महंगे हैं, (uchar1*uchar2) >> 4गैर-साइन-विस्तारित शिफ्ट का उपयोग करके गणना कर सकते हैं । दुर्भाग्य से, कुछ संकलक सिर्फ नतीजों के लिए नहीं, बल्कि ऑपरेंड्स के लिए भी विस्तार का विस्तार करते हैं।
सुपरकैट

2

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


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

दुर्भाग्य से, हालांकि, इसे ऐसे चारों ओर घुमा दिया गया है, यहां तक ​​कि कोड भी जो जानता है कि यह एक प्रोसेसर पर चल रहा है जो किसी विशेष मामले में कुछ उपयोगी होगा, इस तरह के व्यवहार का लाभ नहीं उठा सकता है, क्योंकि संकलक इस तथ्य का उपयोग कर सकते हैं कि सी मानक doesn। कोड में विचित्र-विश्व के पुनर्लेखन को लागू करने के लिए व्यवहार को (हालांकि प्लेटफॉर्म होगा) निर्दिष्ट न करें।
सुपरकैट

1

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


या आप सभी संकेत के रूप में आवंटित कर सकते हैं weak_ptrऔर एक पॉइंटर के सभी संदर्भों को शून्य कर सकते हैं जो deleteघ मिलता है ... ओह रुको, हम कचरा संग्रह के लिए आ रहे हैं: /
Matthieu M.

boost::weak_ptrइस उपयोग पैटर्न के साथ शुरू करने के लिए कार्यान्वयन बहुत अच्छा टेम्पलेट है। weak_ptrsबाहरी रूप से ट्रैकिंग और अशक्त करने के बजाय , weak_ptrबस एक shared_ptrकमजोर गिनती में योगदान देता है , और कमजोर गिनती मूल रूप से सूचक के लिए एक मात्र है। इस प्रकार, आप इसे shared_ptrबिना हटाए तुरंत हटा सकते हैं। यह सही नहीं है (आप अभी भी बहुत सारे समाप्त हो चुके हैं weak_ptrजो shared_countकिसी अच्छे कारण के लिए अंतर्निहित बनाए रख सकते हैं ) लेकिन कम से कम यह तेज और कुशल है।
शराबी

0

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

यह मूल रूप से आपको दो विकल्प देता है:

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

2) के लिए नियमों का एक सेट है जब एक पॉइंटर को एक वेरिएबल को उर्फ ​​करने की अनुमति दी जाती है और जब कंपाइलर को यह मानने की अनुमति दी जाती है कि पॉइंटर एक वेरिएबल को अलियास नहीं करता है।

C विकल्प 2 के लिए चयन करता है, क्योंकि 1 प्रदर्शन के लिए भयानक होगा। लेकिन फिर, क्या होता है अगर एक संकेतक एक तरह से सी नियमों को प्रतिबंधित करता है? चूंकि प्रभाव इस बात पर निर्भर करता है कि संकलक ने वास्तव में चर को रजिस्टर में संग्रहीत किया है या नहीं, सी मानक के लिए निश्चित रूप से अन्य परिणामों की गारंटी देने का कोई तरीका नहीं है।


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

उदाहरण के लिए, यदि कुछ कोड foo42 से सेट करता है , और फिर एक विधि को कॉल करता है जो foo44 में सेट करने के लिए अवैध रूप से संशोधित पॉइंटर का उपयोग करता है , तो मुझे यह कहने के लिए लाभ मिल सकता है कि जब तक अगले "वैध" लिखने के लिए foo, इसे पढ़ने का प्रयास वैध रूप से नहीं हो सकता है उपज ४२ या ४४, और एक अभिव्यक्ति की तरह foo+fooor६ भी प्राप्त कर सकता है, लेकिन मुझे कंपाइलर को विस्तारित और यहां तक ​​कि पूर्वव्यापी अनुमान बनाने की अनुमति देने के लिए बहुत कम लाभ दिखाई देता है, अपरिभाषित व्यवहार को बदलना जिनके प्रशंसनीय "प्राकृतिक" व्यवहार सभी सौम्य थे, एक लाइसेंस में निरर्थक कोड उत्पन्न करने के लिए।
18

0

ऐतिहासिक रूप से, अपरिभाषित व्यवहार के दो प्राथमिक उद्देश्य थे:

  1. संकलक लेखकों की आवश्यकता से बचने के लिए उन परिस्थितियों को संभालने के लिए कोड उत्पन्न करने के लिए जो कभी होने वाली नहीं थीं।

  2. इस संभावना के लिए अनुमति देने के लिए कि कोड की अनुपस्थिति में ऐसी शर्तों को स्पष्ट रूप से संभालने के लिए, कार्यान्वयन में विभिन्न प्रकार के "प्राकृतिक" व्यवहार हो सकते हैं, जो कुछ मामलों में उपयोगी होंगे।

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

दुर्भाग्य से, कुछ संकलक लेखकों ने दर्शन लिया है कि संकलक को अपने रास्ते से हटकर ऐसी परिस्थितियों का पता लगाना चाहिए जो अपरिभाषित व्यवहार को उद्घाटित करें और, यह मानते हुए कि ऐसी स्थितियां कभी भी नहीं हो सकती हैं, उससे विस्तारित निष्कर्ष निकालें। इस प्रकार, 32-बिट वाले सिस्टम पर int, जैसे कोड दिया गया है:

uint32_t foo(uint16_t q, int *p)
{
  if (q > 46340)
    *p++;
  return q*q;
}

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


-6

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


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