दूर एक "समय (1);" C ++ 0x में


153

अद्यतन, नीचे देखें!

मैंने सुना है और पढ़ा है कि C ++ 0x एक कंपाइलर को निम्नलिखित स्निपेट के लिए "हैलो" प्रिंट करने की अनुमति देता है

#include <iostream>

int main() {
  while(1) 
    ;
  std::cout << "Hello" << std::endl;
}

यह स्पष्ट रूप से धागे और अनुकूलन क्षमताओं के साथ कुछ करना है। यह मुझे लगता है कि यह कई लोगों को आश्चर्यचकित कर सकता है।

क्या किसी के पास इस बात की अच्छी व्याख्या है कि अनुमति देना क्यों आवश्यक था? संदर्भ के लिए, सबसे हालिया C ++ 0x ड्राफ्ट में कहा गया है6.5/5

एक लूप जो, एक स्टेटमेंट के मामले में फॉर-इन-स्टेटमेंट के बाहर,

  • लाइब्रेरी I / O फ़ंक्शन के लिए कोई कॉल नहीं करता है, और
  • अस्थिर वस्तुओं तक पहुँच या उन्हें संशोधित नहीं करता है, और
  • कोई सिंक्रनाइज़ेशन ऑपरेशन (1.10) या परमाणु संचालन (क्लाज 29) करता है

समाप्त करने के लिए कार्यान्वयन द्वारा ग्रहण किया जा सकता है। [नोट: यह संकलक transfor- mations, जैसे कि खाली छोरों को हटाने की अनुमति देने का इरादा है, तब भी जब समाप्ति को साबित नहीं किया जा सकता है। - अंतिम नोट]

संपादित करें:

यह मानक लेख उस मानक पाठ के बारे में कहता है

दुर्भाग्य से, शब्द "अपरिभाषित व्यवहार" का उपयोग नहीं किया जाता है। हालांकि, कभी भी मानक कहता है "संकलक पी मान सकता है," यह निहित है कि एक कार्यक्रम जिसमें संपत्ति नहीं है-पी में अपरिभाषित शब्दार्थ है।

क्या यह सही है, और संकलक को उपरोक्त कार्यक्रम के लिए "बाय" प्रिंट करने की अनुमति है?


यहाँ एक और भी अधिक व्यावहारिक थ्रेड है , जो C के अनुरूप परिवर्तन के बारे में है, गाइ द्वारा शुरू किया गया उपरोक्त उपरोक्त लेख। अन्य उपयोगी तथ्यों के बीच, वे एक समाधान प्रस्तुत करते हैं जो C ++ 0x पर भी लागू होता है ( अपडेट करें : यह n3225 के साथ अब और काम नहीं करेगा - नीचे देखें!)

endless:
  goto endless;

एक कंपाइलर को इसे दूर अनुकूलित करने की अनुमति नहीं है, ऐसा लगता है, क्योंकि यह लूप नहीं है, लेकिन एक छलांग है। एक और लड़का C ++ 0x और C201X में प्रस्तावित बदलाव का सारांश प्रस्तुत करता है

लूप लिखने से, प्रोग्रामर या तो जोर दे रहा है कि लूप दृश्य व्यवहार के साथ कुछ करता है (I / O निष्पादित करता है, अस्थिर वस्तुओं तक पहुंचता है, या सिंक्रनाइज़ेशन या परमाणु संचालन करता है), या कि यह अंततः समाप्त हो जाता है। यदि मैं बिना किसी दुष्प्रभाव के एक अनंत लूप लिखकर उस धारणा का उल्लंघन करता हूं, तो मैं संकलक से झूठ बोल रहा हूं, और मेरे कार्यक्रम का व्यवहार अपरिभाषित है। (यदि मैं भाग्यशाली हूं, तो संकलक मुझे इसके बारे में चेतावनी दे सकता है।) भाषा प्रदान नहीं करती है (अब प्रदान नहीं करता है?) दृश्यमान व्यवहार के बिना एक अनंत लूप को व्यक्त करने का एक तरीका है।


3.132 पर n3225 के साथ अपडेट करें: समिति ने पाठ को 1.10 / 24 पर स्थानांतरित किया और कहा

कार्यान्वयन यह मान सकता है कि कोई भी धागा अंततः निम्नलिखित में से एक करेगा:

  • समाप्त कर दें,
  • लाइब्रेरी I / O फ़ंक्शन को कॉल करें,
  • एक वाष्पशील वस्तु तक पहुंच या संशोधन, या
  • एक सिंक्रनाइज़ेशन ऑपरेशन या एक परमाणु ऑपरेशन करें।

gotoचाल होगा नहीं अब और काम करते हैं!


4
while(1) { MyMysteriousFunction(); }उस रहस्यमयी कार्य की परिभाषा को जाने बिना स्वतंत्र रूप से अनिवार्य होना चाहिए, है ना? तो हम कैसे निर्धारित कर सकते हैं कि यह किसी भी पुस्तकालय I / O फ़ंक्शन को कॉल करता है? दूसरे शब्दों में: निश्चित रूप से कि पहली गोली को काम पर रखा जा सकता है, फ़ंक्शन को कोई कॉल नहीं करता है
डैनियल ईयरविकर

19
@ डैनियल: यदि यह फ़ंक्शन की परिभाषा तक पहुंच रखता है, तो यह बहुत सी चीजें साबित कर सकता है। इंटरप्रोड्यूरल ऑप्टिमाइज़ेशन जैसी कोई चीज होती है।
पोटैटोसवाटर

3
अभी, सी ++ 03 में, एक संकलक परिवर्तन करने की अनुमति दी है int x = 1; for(int i = 0; i < 10; ++i) do_something(&i); x++;में for(int i = 0; i < 10; ++i) do_something(&i); int x = 2;? या संभवतः दूसरा तरीका, लूप xसे 2पहले आरंभीकृत होने के साथ । यह बता सकता है कि do_somethingइसके मूल्य के बारे में कोई परवाह नहीं है x, इसलिए इसका पूरी तरह से सुरक्षित अनुकूलन है, यदि आप इस तरह के परिवर्तन do_somethingका मूल्य पैदा नहीं करते iहैं जो आप अनंत लूप में समाप्त करते हैं।
डेनिस ज़िकफ़ोज़

4
तो क्या इसका मतलब यह है कि main() { start_daemon_thread(); while(1) { sleep(1000); } }बैकग्राउंड थ्रेड में मेरे डेमन को चलाने के बजाय तुरंत बाहर निकल सकते हैं?
गेबे

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

जवाबों:


33

क्या किसी के पास इस बात की अच्छी व्याख्या है कि अनुमति देना क्यों आवश्यक था?

हां, हंस बोएहम एन 1528 में इसके लिए एक तर्क प्रदान करता है : अनंत छोरों के लिए अपरिभाषित व्यवहार क्यों? , हालांकि यह WG14 दस्तावेज़ है, औचित्य C ++ पर भी लागू होता है और दस्तावेज़ WG14 और WG21 दोनों को संदर्भित करता है:

जैसा कि N1509 सही ढंग से बताता है, वर्तमान मसौदा अनिवार्य रूप से 6.8.5p6 में अनंत छोरों को अपरिभाषित व्यवहार देता है। ऐसा करने के लिए एक प्रमुख मुद्दा यह है कि यह एक संभावित गैर-समाप्ति लूप में कोड को स्थानांतरित करने की अनुमति देता है। उदाहरण के लिए, मान लें कि हमारे पास निम्नलिखित लूप हैं, जहां गणना और count2 वैश्विक चर हैं (या उनका पता लिया गया है), और p एक स्थानीय चर है, जिसका पता नहीं लिया गया है:

for (p = q; p != 0; p = p -> next) {
    ++count;
}
for (p = q; p != 0; p = p -> next) {
    ++count2;
}

क्या इन दो छोरों को मिलाया जा सकता है और निम्नलिखित लूप द्वारा प्रतिस्थापित किया जा सकता है?

for (p = q; p != 0; p = p -> next) {
        ++count;
        ++count2;
}

अनंत छोरों के लिए 6.8.5p6 में विशेष वितरण के बिना, यह अस्वीकृत हो जाएगा: यदि पहला लूप समाप्त नहीं होता है क्योंकि क्यू एक परिपत्र सूची में इंगित करता है, तो मूल कभी भी count2 को नहीं लिखता है। इस प्रकार इसे दूसरे थ्रेड के साथ समानांतर में चलाया जा सकता है जो काउंट 2 तक पहुंच या अपडेट करता है। यह अब परिवर्तित संस्करण के साथ सुरक्षित नहीं है जो अनंत लूप के बावजूद count2 तक पहुंच प्राप्त करता है। इस प्रकार परिवर्तन संभवतः एक डेटा दौड़ का परिचय देता है।

इस तरह के मामलों में, यह बहुत कम संभावना है कि एक संकलक लूप समाप्ति को साबित करने में सक्षम होगा; यह समझना होगा कि क्ष एक एसाइक्लिक सूची में इंगित करता है, जो मुझे लगता है कि अधिकांश मुख्यधारा संकलक की क्षमता से परे है, और अक्सर पूरे कार्यक्रम की जानकारी के बिना असंभव है।

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

पूर्णांक लूप चर के साथ स्पष्ट रूप से लूप भी होते हैं, जिसमें संकलक के लिए समाप्ति साबित करना मुश्किल होता है, और इस प्रकार कंपाइलर के लिए 6.8.5p6 के बिना लूप को पुनर्गठन करना मुश्किल होगा। यहां तक ​​कि कुछ ऐसा भी

for (i = 1; i != 15; i += 2)

या

for (i = 1; i <= 10; i += j)

संभाल करने के लिए nontrivial लगता है। (पूर्व मामले में, समाप्ति साबित करने के लिए कुछ बुनियादी संख्या सिद्धांत की आवश्यकता है। बाद के मामले में, हमें ऐसा करने के लिए j के संभावित मूल्यों के बारे में कुछ जानना होगा। अहस्ताक्षरित पूर्णांक के लिए लपेटें इस तर्क के कुछ को और जटिल कर सकते हैं। )

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

C के साथ एक बड़ा अंतर यह है कि C11 भावों को नियंत्रित करने के लिए एक अपवाद प्रदान करता है जो निरंतर अभिव्यक्तियाँ हैं जो C ++ से अलग है और आपके विशिष्ट उदाहरण को C11 में अच्छी तरह से परिभाषित करता है।


1
क्या कोई सुरक्षित और उपयोगी अनुकूलन है जो कि वर्तमान भाषा द्वारा सुविधाजनक है जिसे केवल कहने से सुविधा नहीं होगी "यदि लूप की समाप्ति किसी भी वस्तु की स्थिति पर निर्भर करती है, तो लूप को निष्पादित करने के लिए आवश्यक समय नहीं माना जाता है; अवलोकनीय दुष्परिणाम, भले ही ऐसा समय अनंत हो ”। do { x = slowFunctionWithNoSideEffects(x);} while(x != 23);लूप के बाद उत्थापन कोड को देखते हुए , जो निर्भर नहीं xकरेगा, सुरक्षित और उचित प्रतीत होगा, लेकिन एक संकलक को x==23इस तरह के कोड में ग्रहण करने की अनुमति देना उपयोगी के लिए अधिक खतरनाक लगता है।
सुपरकैट

47

मेरे लिए, प्रासंगिक औचित्य है:

इसका उद्देश्य संकलक को बदलने की अनुमति देना है, जैसे कि खाली छोरों को हटाना, तब भी जब समाप्ति को सिद्ध नहीं किया जा सकता है।

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

संपादित करें: गूंगा उदाहरण:

while (complicated_condition()) {
    x = complicated_but_externally_invisible_operation(x);
}
complex_io_operation();
cout << "Results:" << endl;
cout << x << endl;

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

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

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

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

एक समान तर्क पर विचार करें: "कंपाइलर मान सकता है कि एक चर x केवल अनुक्रम बिंदुओं के बीच एक बार सबसे अधिक असाइन किया गया है" "अनुक्रम बिंदु अपरिभाषित के बीच एक से अधिक बार एक्स को असाइन करने के बराबर है"।


"प्रोविंग 1) बहुत आसान है" - वास्तव में यह संकलक के लिए 3 शर्तों से तुरंत पालन नहीं करता है, जोहान्स के बारे में जोज़ के बारे में पूछ रहे हैं, के तहत लूप समाप्ति की अनुमति दी जाए? मुझे लगता है कि वे "शायद हमेशा के लिए कताई के अलावा" लूप का कोई अवलोकन प्रभाव नहीं है, और क्लॉज सुनिश्चित करता है कि "हमेशा के लिए कताई" ऐसे छोरों के लिए व्यवहार की गारंटी नहीं है।
स्टीव जेसप

@ सच: यदि लूप समाप्त नहीं होता है तो यह आसान है; लेकिन अगर लूप समाप्त हो जाता है, तो इसके पास nontrivial व्यवहार हो सकता है जो प्रसंस्करण को प्रभावित करता है complex_io_operation
फिलिप पॉटर

उफ़, हाँ, मैंने याद किया कि यह गैर-वाष्पशील लोकल / उपनाम / जो भी IO ऑप में उपयोग किया जाता है, को संशोधित कर सकता है। तो आप सही हैं: हालाँकि यह जरूरी नहीं है, ऐसे कई मामले हैं जिनमें कंपाइलर कर सकते हैं और यह साबित करते हैं कि ऐसा कोई संशोधन नहीं हुआ है।
स्टीव जेसप

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

1
@ सुपरकैट: आप जो वर्णन करते हैं वह व्यवहार में क्या होगा, लेकिन यह नहीं है कि मसौदा मानक की आवश्यकता क्या है। हम यह नहीं मान सकते कि कंपाइलर को पता नहीं है कि लूप समाप्त हो जाएगा। संकलक तो करता है पाश पता समाप्त नहीं होगा, यह जो कुछ भी यह पसंद करती है कर सकते हैं। DS9K जाएगा के लिए नाक राक्षसों बनाने के किसी भी नहीं आई / ओ आदि के साथ अनंत लूप (इसलिए, DS9K हल हॉल्टिंग समस्या।)
फिलिप पॉटर

15

मुझे लगता है कि सही व्याख्या आपके संपादन में से एक है: खाली अनंत लूप अपरिभाषित व्यवहार हैं।

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

यदि अनंत लूप यूबी हैं, तो इसका मतलब है कि गैर-समाप्ति कार्यक्रम को सार्थक नहीं माना जाता है: सी ++ 0x के अनुसार, उनके पास कोई शब्दार्थ नहीं है।

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


7
"खाली अनंत छोरों अपरिभाषित व्यवहार कर रहे हैं"? एलन ट्यूरिंग अलग करने के लिए भीख माँगती है, लेकिन केवल जब वह अपनी कब्र में घूमती है।
डोनाल्ड फेलो

11
@ डॉनल: मैंने कभी ट्यूरिंग मशीन में इसके शब्दार्थ के बारे में कुछ नहीं कहा। हम एक अनंत लूप के शब्दार्थ पर चर्चा कर रहे हैं जिसमें C ++ में कोई साइड इफेक्ट नहीं है । और जैसा कि मैंने इसे पढ़ा है, C ++ 0x यह कहना चाहता है कि ऐसे लूप अपरिभाषित हैं।
jalf

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

1
इसका मतलब यह है कि C ++ 0x एम्बेडेड उपकरणों के लिए अनुकूल नहीं है? लगभग सभी एम्बेडेड डिवाइस नॉन-टर्मिनेटिंग हैं और एक बड़े वसा के अंदर अपना काम करते हैं while(1){...}। वे नियमित रूप while(1);से वॉचडॉग-असिस्टेड रीसेट को लागू करने के लिए भी उपयोग करते हैं।
vsz

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

8

मुझे लगता है कि यह इस प्रकार के प्रश्न की तर्ज पर है , जो एक और सूत्र का संदर्भ देता है । अनुकूलन कभी-कभी खाली छोरों को हटा सकता है।


3
अच्छा प्रश्न। उस आदमी की तरह लगता है कि इस अनुच्छेद कि संकलक कारण के लिए अनुमति देता है बिल्कुल समस्या थी। किसी एक उत्तर से जुड़ी चर्चा में, यह लिखा है कि "दुर्भाग्य से, 'अपरिभाषित व्यवहार' शब्द का उपयोग नहीं किया जाता है। हालांकि, कभी भी मानक कहते हैं कि 'कंपाइलर पी मान सकता है,' यह निहित है कि एक कार्यक्रम है। संपत्ति नहीं- P में अपरिभाषित शब्दार्थ है। " । इसने मुझे चौंका दिया। क्या इसका मतलब यह है कि ऊपर दिए गए मेरे उदाहरण कार्यक्रम में अपरिभाषित व्यवहार है, और कहीं से भी सिर्फ सेगफॉल्ट हो सकता है?
जोहान्स स्काउब -

@ जोहान्स: पाठ "माना जा सकता है" मेरे द्वारा तैयार किए गए मसौदे में कहीं और नहीं होता है, और "मान सकते हैं" केवल दो बार होता है। हालांकि मैंने इसे एक खोज फ़ंक्शन के साथ चेक किया था जो लाइन ब्रेक के पार मैच करने में विफल रहता है, इसलिए मुझे कुछ याद हो सकता है। इसलिए मुझे यकीन नहीं है कि लेखक के सामान्यीकरण को सबूतों पर वार किया गया है, लेकिन एक गणितज्ञ के रूप में मुझे तर्क के तर्क को स्वीकार करना होगा, कि यदि कंपाइलर कुछ गलत मानता है तो सामान्य तौर पर यह कुछ भी घटा सकता है ...
स्टीव जेसी

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

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

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

8

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

मेरा मानना ​​है कि यह सही तरीका है। भाषा कल्पना निष्पादन के आदेश को लागू करने के तरीकों को परिभाषित करती है। यदि आप एक अनंत लूप चाहते हैं, जिसे दोबारा नहीं लिखा जा सकता है, तो इसे लिखें:

volatile int dummy_side_effect;

while (1) {
    dummy_side_effect = 0;
}

printf("Never prints.\n");

2
@ जोहान्सचैब-लिट: यदि कोई लूप - अंतहीन या नहीं - निष्पादन के दौरान कोई अस्थिर चर नहीं पढ़ता है या नहीं लिखता है, और ऐसा करने वाले किसी भी फ़ंक्शन को कॉल नहीं करता है, तो एक कंपाइलर लूप के किसी भी हिस्से को हटाने के लिए स्वतंत्र है। उसमें कुछ गणना करने का पहला प्रयास। यह देखते हुए unsigned int dummy; while(1){dummy++;} fprintf(stderror,"Hey\r\n"); fprintf(stderror,"Result was %u\r\n",dummy);, पहला fprintfअमल कर सकता है, लेकिन दूसरा नहीं कर सकता (कंपाइलर dummyदोनों के बीच की गणना को आगे fprintfबढ़ा सकता है, लेकिन उस अतीत को नहीं जो उसके मूल्य को प्रिंट करता है)।
सुपरकाट

1

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

शून्य गवाही (इंट एन)
{
  int a = 1, b = 1, c = 1;
  जबकि (pow (a, n) + pow (b, n)! = pow (c, n))
  {
    if (b> a) a ++; और अगर (c> b) {a = 1; ख ++}; और {a = 1; ख = 1; c ++};
  }
  प्रिंटफ ("परिणाम" है);
  प्रिंटफ ("% d /% d /% d", ए, बी, सी);
}

जैसा

शून्य गवाही (इंट एन)
{
  अगर (fork_is_first_thread ())
  {
    int a = 1, b = 1, c = 1;
    जबकि (pow (a, n) + pow (b, n)! = pow (c, n))
    {
      if (b> a) a ++; और अगर (c> b) {a = 1; ख ++}; और {a = 1; ख = 1; c ++};
    }
    signal_other_thread_and_die ();
  }
  और // दूसरा धागा
  {
    प्रिंटफ ("परिणाम" है);
    wait_for_other_thread ();
  }
  प्रिंटफ ("% d /% d /% d", ए, बी, सी);
}

आम तौर पर अनुचित नहीं है, हालांकि मुझे चिंता हो सकती है कि:

  int total = 0;
  (i = 0; num_reps> i; i ++) के लिए
  {
    update_progress_bar (i);
    कुल + = do_something_slow_with_no_side_effects (i);
  }
  show_result (कुल);

बन जाएगा

  int total = 0;
  अगर (fork_is_first_thread ())
  {
    (i = 0; num_reps> i; i ++) के लिए
      कुल + = do_something_slow_with_no_side_effects (i);
    signal_other_thread_and_die ();
  }
  अन्य
  {
    (i = 0; num_reps> i; i ++) के लिए
      update_progress_bar (i);
    wait_for_other_thread ();
  }
  show_result (कुल);

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


मुझे लगता है कि आपका प्रगति बार मामला अलग नहीं किया जा सकता है, क्योंकि प्रगति पट्टी प्रदर्शित करना एक पुस्तकालय I / O कॉल है। ऑप्टिमाइज़ेशन को दृश्य व्यवहार को इस तरह से नहीं बदलना चाहिए।
फिलिप पॉटर

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

1
यह उन सभी प्रगति पट्टियों की व्याख्या करता है जो 0 से 100 तक तेज होती हैं और फिर उम्र के लिए लटक जाती हैं;)
पॉल

0

यह गैर-तुच्छ मामलों के लिए संकलक के लिए निर्णायक नहीं है यदि यह एक अनंत लूप है।

विभिन्न मामलों में, ऐसा हो सकता है कि आपका आशावादी आपके कोड के लिए बेहतर जटिलता वर्ग तक पहुंच जाएगा (जैसे कि यह O (n ^ 2) था और आपको अनुकूलन के बाद O (n) या O (1) मिलता है।

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


एक और बात: मैंने कभी ऐसा कोई वैध उदाहरण नहीं देखा है जहाँ आपको अनंत लूप की आवश्यकता होती है जो कुछ भी नहीं करता है।

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

यदि आपको कोई मान्य / अच्छा उदाहरण पता है जहाँ आपको एक अनंत लूप की आवश्यकता है जो कुछ नहीं करता है, तो कृपया मुझे बताएं।


1
उदाहरण के लिए जहां आप एक अनंत लूप चाहते हैं: एक एम्बेडेड सिस्टम जहां आप प्रदर्शन कारणों से सोना नहीं चाहते हैं और सभी कोड एक या दो बाधाएं हैं?
JCx

@JCx in Standard C इंटरप्ट को एक ध्वज सेट करना चाहिए जो मुख्य लूप की जांच करता है, इसलिए मुख्य लूप में सेट किए जाने वाले झंडे के मामले में अवलोकन योग्य व्यवहार होगा। इंटरप्ट में पर्याप्त कोड चलाना गैर-पोर्टेबल है।
एमएम

-1

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

मैं दूसरे शब्दों में, आपके ग्लोबल्स को अस्थिर बनाता हूं - साथ ही सूचक / संदर्भ के माध्यम से इस तरह के लूप में तर्क दिए गए हैं।


यदि वे अन्य थ्रेड्स के साथ बातचीत कर रहे हैं, तो आपको उन्हें अस्थिर नहीं करना चाहिए, उन्हें एटमिक्स बनाना चाहिए या उन्हें लॉक से सुरक्षित रखना चाहिए।
21

1
Thjs भयानक सलाह है। उन्हें बनाना volatileन तो आवश्यक है और न ही पर्याप्त है, और यह प्रदर्शन को नाटकीय रूप से प्रभावित करता है।
डेविड श्वार्ट्ज 19
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.