यह एक क्लैंग बग है
... एक अनंत लूप वाले फ़ंक्शन को सम्मिलित करते समय। while(1);
मुख्य रूप से सीधे प्रकट होने पर व्यवहार भिन्न होता है, जो मुझे बहुत छोटी लगता है।
सारांश और लिंक के लिए @ अर्नवियन का उत्तर देखें । इस उत्तर के बाकी हिस्सों को मैंने पुष्टि करने से पहले लिखा था कि यह एक बग था, अकेले एक ज्ञात बग दें।
शीर्षक प्रश्न का उत्तर देने के लिए: मैं एक अनंत खाली लूप कैसे बनाऊंगा जिसे दूर नहीं किया जाएगा? ? - क्लेग 3.9 में और बाद में इस बग के आसपास काम करने के
लिए die()
एक मैक्रो, फ़ंक्शन नहीं । (पहले के क्लैंग संस्करण या तो लूप रखते हैं या एक का उत्सर्जन करते हैंcall
अनंत लूप के साथ समारोह की एक गैर इनलाइन संस्करण के लिए।) प्रकट होता है सुरक्षित है यही कारण है कि यहां तक कि अगर print;while(1);print;
में समारोह inlines अपने फोन करने वाले ( Godbolt )। -std=gnu11
बनाम -std=gnu99
कुछ भी नहीं बदलता है।
यदि आप केवल GNU C की परवाह करते हैं, तो P__J ____asm__("");
का लूप अंदर भी काम करता है, और इसे समझने वाले किसी भी कंपाइलर के लिए किसी भी आसपास के कोड के अनुकूलन को नुकसान नहीं पहुंचाना चाहिए। जीएनयू सी बेसिक एएसएम के कथन स्पष्ट रूप से हैंvolatile
, इसलिए यह एक दृश्य साइड-इफ़ेक्ट के रूप में गिना जाता है जिसे सी एब्सट्रैक्ट मशीन में "निष्पादित" करना होता है। (और हाँ, क्लेंग जीसीसी मैनुअल द्वारा प्रलेखित के रूप में सी की GNU बोली को लागू करता है।)
कुछ लोगों ने तर्क दिया है कि खाली अनंत लूप को अनुकूलित करना कानूनी हो सकता है। मैं सहमत नहीं हूँ 1 , लेकिन फिर भी अगर हम कि स्वीकार करते हैं, यह नहीं कर सकता भी कानूनी बजना मान के लिए करने के लिए लूप के बाद बयान पहुंचा नहीं जा सकता, हो और अगले समारोह में समारोह के अंत में निष्पादन गिरावट है, या कचरा में यादृच्छिक निर्देश के रूप में डिकोड करता है।
(यह क्लैंग ++ के लिए मानक-अनुरूप होगा (लेकिन अभी भी बहुत उपयोगी नहीं है), बिना किसी साइड इफेक्ट के अनंत लूप सी ++ में यूबी हैं, लेकिन सी नहीं।
(1), सी में अपरिभाषित व्यवहार? यूबी कंपाइलर मूल रूप से कुछ भी उत्सर्जन करने देता है। निष्पादन के एक मार्ग पर कोड के लिए जो निश्चित रूप से UB का सामना करेगा। asm
लूप में एक कथन C ++ के लिए इस UB से बचता है। लेकिन व्यवहार में, C ++ के रूप में क्लैंग संकलन निरंतर अभिव्यक्ति को नहीं हटाता है जब इनलाइनिंग को छोड़कर अनंत लूप खोले जाते हैं, जब वे सी। के रूप में संकलन)
मैन्युअल रूप से inlining while(1);
परिवर्तन होता है कि क्लैंग इसे कैसे संकलित करता है: असीम लूप में मौजूद। यह वही है जो हम एक नियम-वकील POV से उम्मीद करेंगे।
#include <stdio.h>
int main() {
printf("begin\n");
while(1);
//infloop_nonconst(1);
//infloop();
printf("unreachable\n");
}
गॉडबोल्ट कंपाइलर एक्सप्लोरर पर, क्लेंग 9.0 -ओ 3 संकलन के रूप में सी ( -xc
) के लिए x86-64:
main: # @main
push rax # re-align the stack by 16
mov edi, offset .Lstr # non-PIE executable can use 32-bit absolute addresses
call puts
.LBB3_1: # =>This Inner Loop Header: Depth=1
jmp .LBB3_1 # infinite loop
.section .rodata
...
.Lstr:
.asciz "begin"
एक ही विकल्प के साथ एक ही संकलक एक संकलन करता है main
infloop() { while(1); }
उसी को पहले कॉलputs
, लेकिन फिर main
उस बिंदु के लिए निर्देश छोड़ना बंद कर देता है । इसलिए जैसा कि मैंने कहा, फंक्शन सिर्फ फंक्शन के अंत में पड़ता है, जो भी फ़ंक्शन होता है (लेकिन फंक्शन एंट्री के लिए स्टैक मिसलॉन्ग किया जाता है, इसलिए यह एक वैध टेलकॉल भी नहीं है)।
मान्य विकल्प होगा
- एक
label: jmp label
अनंत लूप का उत्सर्जन करें
- या (यदि हम स्वीकार करते हैं कि अनंत लूप को हटाया जा सकता है) 2 वें स्ट्रिंग को प्रिंट करने के लिए एक और कॉल का उत्सर्जन करें, और फिर
return 0
सेmain
।
"अप्राप्य" को प्रिंट किए बिना क्रैश करना या अन्यथा जारी रखना C11 कार्यान्वयन के लिए स्पष्ट रूप से ठीक नहीं है, जब तक कि यूबी नहीं है कि मैंने ध्यान नहीं दिया है।
फुटनोट 1:
रिकॉर्ड के लिए, मैं इससे सहमत हूं @ लुंडिन के उत्तर से जो सबूत के लिए मानक का हवाला देता है कि C11 निरंतर-अभिव्यक्ति अनंत छोरों के लिए समाप्ति की धारणा की अनुमति नहीं देता है, भले ही वे खाली हों (I / O, वाष्पशील, सिंक्रनाइज़ेशन, या अन्य नहीं दृश्यमान प्रभाव)।
यह उन स्थितियों का समूह है जो लूप को खाली एसम लूप में संकलित करने देता है को एक सामान्य सीपीयू के लिए एक । (भले ही शरीर स्रोत में खाली न हो, लेकिन चर के असाइनमेंट डेटा-रेस UB के बिना अन्य थ्रेड्स या सिग्नल हैंडलर को दिखाई नहीं दे सकते हैं जबकि लूप चल रहा है। इसलिए एक अनुरूप कार्यान्वयन ऐसे लूप निकायों को हटा सकता है यदि यह चाहता था। इसके बाद, यह सवाल छोड़ देता है कि क्या लूप को स्वयं हटाया जा सकता है। ISO C11 स्पष्ट रूप से नहीं कहता है।)
यह देखते हुए कि C11 उस मामले को एकल के रूप में प्रस्तुत करता है जहां कार्यान्वयन लूप को समाप्त नहीं कर सकता है (और यह कि यह यूबी नहीं है), यह स्पष्ट लगता है कि वे लूप को रन-टाइम पर उपस्थित होने का इरादा रखते हैं। एक कार्यान्वयन जो सीपीयू को एक निष्पादन मॉडल के साथ लक्षित करता है जो अनंत समय में काम नहीं कर सकता है एक खाली निरंतर अनंत लूप को हटाने का कोई औचित्य नहीं है। या यहां तक कि सामान्य तौर पर, सटीक शब्द इस बारे में है कि क्या उन्हें "समाप्त करने के लिए ग्रहण किया जा सकता है" या नहीं। यदि कोई लूप समाप्त नहीं हो सकता है, तो इसका मतलब है कि बाद में कोड उपलब्ध नहीं है, इससे कोई फर्क नहीं पड़ता कि आप क्या तर्क देते हैं गणित और अनन्तताओं के बारे में और किसी काल्पनिक मशीन पर अनंत मात्रा में काम करने में कितना समय लगता है।
इसके अलावा, क्लैंग केवल एक आईएसओ सी आज्ञाकारी डेथक्राफ्ट 9000 नहीं है, यह वास्तविक दुनिया के निचले स्तर के सिस्टम प्रोग्रामिंग के लिए उपयोगी है, जिसमें कर्नेल और एम्बेडेड सामान शामिल हैं। तो क्या आप C11 को हटाने की अनुमति के बारे में तर्क स्वीकार करते हैं या नहीं while(1);
, इससे कोई मतलब नहीं है कि क्लैंग वास्तव में ऐसा करना चाहेगा। यदि आप लिखते हैं while(1);
, कि शायद एक दुर्घटना नहीं थी। दुर्घटना से अनंत को समाप्त करने वाले छोरों को हटाना (रनटाइम चर नियंत्रण अभिव्यक्ति के साथ) उपयोगी हो सकता है, और यह संकलक को ऐसा करने के लिए समझ में आता है।
यह दुर्लभ है कि आप बस अगले अवरोध तक स्पिन करना चाहते हैं, लेकिन अगर आप लिखते हैं कि सी में वह निश्चित रूप से है जो आप होने की उम्मीद करते हैं। (और जीसीसी और क्लैंग में क्या होता है, क्लैंग को छोड़कर जब अनंत लूप एक आवरण समारोह के अंदर होता है)।
उदाहरण के लिए, एक आदिम OS कर्नेल में, जब अनुसूचक के पास चलाने के लिए कोई कार्य नहीं होता है तो वह निष्क्रिय कार्य चला सकता है। इसका पहला कार्यान्वयन हो सकता है while(1);
।
या हार्डवेयर के लिए किसी भी बिजली की बचत निष्क्रिय सुविधा के बिना, कि केवल कार्यान्वयन हो सकता है। (2000 के दशक की शुरुआत तक, मुझे लगता है कि x86 पर दुर्लभ नहीं था। हालाँकि यह hlt
निर्देश मौजूद नहीं था, अगर सीपीयू के कम-शक्ति निष्क्रिय होने की स्थिति होने पर आईडीके ने इसे सार्थक मात्रा में बचाया।