2016-01-13 5 views
44

मैंने a snippet of code on CodeGolf देखा जो कि एक कंपाइलर बम के रूप में है, जहां main को एक विशाल सरणी के रूप में घोषित किया गया है।एक सरणी संकलन के रूप में मुख्य घोषित क्यों करता है?

int main[1] = { 0 }; 

यह और बजना के तहत ठीक संकलित करने के लिए लगता है जीसीसी के तहत केवल एक चेतावनी के साथ:

चेतावनी: मैं निम्नलिखित (गैर बम) संस्करण की कोशिश की 'मुख्य' आम तौर पर एक समारोह [है - Wmain]

परिणामस्वरूप बाइनरी, बेशक, कचरा है।

लेकिन यह बिल्कुल संकलित क्यों होता है? क्या यह सी विनिर्देशन द्वारा भी अनुमति है? अनुभाग मुझे लगता है कि प्रासंगिक है का कहना है:

5.1.2.2.1 कार्यक्रम स्टार्टअप

समारोह कार्यक्रम स्टार्टअप पर बुलाया मुख्य नाम पर है। कार्यान्वयन इस समारोह के लिए कोई प्रोटोटाइप घोषित नहीं करता है। इसे एक प्रकार के int के साथ परिभाषित किया जाएगा और बिना पैरामीटर [...] या दो पैरामीटर [...] या कुछ अन्य कार्यान्वयन-परिभाषित तरीके से परिभाषित किया जाएगा।

क्या "कुछ अन्य कार्यान्वयन-परिभाषित तरीके" में वैश्विक सरणी शामिल है? (मुझे ऐसा लगता है कि कल्पना अभी भी एक समारोह को दर्शाता है।)

यदि नहीं, तो यह एक संकलक विस्तार है? या टूलचेन की एक विशेषता, जो किसी अन्य उद्देश्य की सेवा करती है और उन्होंने इसे फ्रंटेंड के माध्यम से उपलब्ध कराने का फैसला किया?

+1

यह ** संकलित नहीं करता है। आईएसओ सी शून्य आकार के सरणी मना करता है। – Jens

+7

सी विनिर्देशन द्वारा इसकी अनुमति नहीं है। कंपाइलर्स अक्सर सामान को लागू करते हैं जो विनिर्देशन से ढके नहीं होते हैं। –

+0

संबंधित प्रश्न: [एक वैश्विक चर के साथ एक प्रोग्राम मुख्य समारोह के बजाय मुख्य कहलाता है?] (Http://stackoverflow.com/q/32851184/1708801)। मुझे लगता है कि एक कोडगोल्फ सवाल से भी प्रेरित है। –

उत्तर

30

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

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

फ्रीस्टैंडिंग वातावरण में

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

यदि आप इसे सामान्य रूप से लिंक करते हैं तो यह खराब हो जाएगा क्योंकि लिंकर को आमतौर पर प्रतीकों की प्रकृति के बारे में बहुत कम ज्ञान होता है (यह किस प्रकार है या यहां तक ​​कि यह एक फ़ंक्शन या चर है)। इस मामले में लिंकर main पर main नामक चर के लिए कॉल को खुशी से हल करेगा। यदि प्रतीक नहीं मिला है तो इसका परिणाम लिंक त्रुटि होगी।

आप इसे लिंक कर रहे हैं हमेशा की तरह आप मूल रूप से की मेजबानी की आपरेशन में संकलक का उपयोग करने की कोशिश कर रहे हैं और फिर main को परिभाषित नहीं के रूप में आप अपेक्षा कर रहे हैं परिशिष्ट प्रति जे के रूप में अपरिभाषित व्यवहार का मतलब करने के लिए2:

व्यवहार निम्न परिस्थितियों में अपरिभाषित है:

  • ... एक की मेजबानी की वातावरण में
  • कार्यक्रम मुख्य नामक प्रकार्य निर्दिष्ट रूपों में से एक का उपयोग कर को परिभाषित नहीं करता (5.1.2.2.1)

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

+0

यह सिग्विन पर जीसीसी 4.9.3 के साथ जुड़ा हुआ है (ठीक है, एक चेतावनी के साथ): 'int f (int argc, char ** argv) { \t वापसी 0; } char * main = (char *) f; ' –

+0

@ पीटर ए। स्किनाइडर लेकिन अगर यह ठीक चलता है तो यह सिर्फ शुद्ध भाग्य है। सीआरटी-इनिट 'मुख्य' को कॉल करने का प्रयास करेगा, जहां पॉइंटर संग्रहीत किया जाता है और यह क्या इंगित करता है। – skyking

+0

यह लिंक लेकिन segfaults। बीटीडब्ल्यू, मुझे नहीं लगता कि प्रश्न "फ्रीस्टैंडिंग" के साथ बहुत कुछ करना है। उदाहरण के लिए, वीएस 13 में निम्नलिखित संकलन और लिंक (एक डीएलएल): 'नेमस्पेस मेन_ब्यूज्ड { कक्षा कार्यक्रम { int मुख्य = 0; } } '। ऐसा इसलिए है कि मुख्य (और सी # में मुख्य) कीवर्ड नहीं हैं, और सी लिंकर्स गूंगा, गलती, सरल हैं। –

7

main संकलन के बाद - कई अन्य लोगों (वैश्विक कार्यों, वैश्विक चर, आदि) जैसी ऑब्जेक्ट फ़ाइल में बस एक और प्रतीक है।

लिंकर इसके प्रकार के बावजूद main प्रतीक लिंक करेगा। दरअसल, लिंकर सब पर प्रतीक के प्रकार के नहीं देख सकते हैं (वह देख सकते हैं, कि यह .text -section में हालांकि नहीं है, लेकिन वह परवाह नहीं करता;))

जीसीसी का उपयोग करना, मानक एंट्री पॉइंट _start है, जो बदले में रनटाइम पर्यावरण तैयार करने के बाद मुख्य() को कॉल करता है। तो यह पूर्णांक सरणी के पते पर कूद जाएगा, जो आम तौर पर खराब निर्देश, segfault या कुछ अन्य बुरे व्यवहार के परिणामस्वरूप होगा।

इस कोर्स के पास सी-मानक के साथ कुछ लेना देना नहीं है।

+0

स्काईकिंग के उत्तर लिंक पर टिप्पणी के रूप में मैंने पोस्ट किया गया न्यूनतम उदाहरण लेकिन segfaults। उस काम को करने के लिए कोई चिमटा, इनलाइन असेंबलर या ऐसे के साथ कहें? –

+0

@ PeterA.Schneider यह segfaults क्योंकि यह सूचक के _address_ पर कूद जाएगा, न कि इसकी सामग्री के लिए। – Ctx

+0

धन्यवाद! मुझे लगता है कि मुझे अभी भी इन टूलचेन्स के सी फ्रंटेंड को फेंकने की उम्मीद है, भले ही लिंकर ऑब्जेक्ट फाइलों को देखता है, परवाह नहीं करता है। –

2

यह केवल संकलित (और काम करता है क्योंकि linkers कभी-कभी केवल नाम प्रतीकों में से, न कि उनका प्रकार के लिए देखभाल) क्योंकि आप उचित विकल्प का उपयोग नहीं करते।

$ gcc -std=c89 -pedantic -Wall x.c 
x.c:1:5: warning: ISO C forbids zero-size array ‘main’ [-Wpedantic] 
int main[0]; 
    ^
x.c:1:5: warning: ‘main’ is usually a function [-Wmain] 
+2

यह अभी भी संकलित और लिंक है। केवल अंतर यह है कि यह आपको चेतावनी देता है कि 'मुख्य' आमतौर पर एक कार्य होता है (फिर यह जारी रहता है और वैसे भी लिंक करता है)। – skyking

+0

@skyking आप संकलित/लिंक विफल होना चाहते हैं? तब 'वाइरर' जोड़ें। – Jens

+0

लेकिन फिर (अन्य) मान्य सी प्रोग्राम भी संकलित करने में असफल हो जाएंगे। – skyking

20

यदि आप रुचि रखते हैं कि मुख्य सरणी में प्रोग्राम कैसे बनाएं: https://jroweboy.github.io/c/asm/2015/01/26/when-is-main-not-a-function.html। उदाहरण स्रोत में केवल एक char (और बाद में int) सरणी शामिल है जिसे main कहा जाता है जो मशीन निर्देशों से भरा होता है।

मुख्य कदम और समस्या आई थी:

  • एक gdb स्मृति डम्प से प्राप्त एक मुख्य कार्य की मशीन के निर्देशों और यह स्थिरांक की घोषणा के द्वारा main[] निष्पादन में डेटा सरणी में कॉपी
  • टैग (डेटा स्पष्ट रूप से या तो लिखने योग्य या निष्पादन योग्य है)
  • अंतिम विवरण: वास्तविक स्ट्रिंग डेटा के लिए पता बदलें।

जिसके परिणामस्वरूप सी कोड सिर्फ

const int main[] = { 
    -443987883, 440, 113408, -1922629632, 
    4149, 899584, 84869120, 15544, 
    266023168, 1818576901, 1461743468, 1684828783, 
    -1017312735 
}; 

लेकिन है एक 64 बिट पीसी पर एक निष्पादन कार्यक्रम में परिणाम:

$ gcc -Wall final_array.c -o sixth 
final_array.c:1:11: warning: ‘main’ is usually a function [-Wmain] 
const int main[] = { 
     ^
$ ./sixth 
Hello World! 
5

समस्या यह है कि main एक आरक्षित पहचानकर्ता नहीं है। सी मानक केवल इतना कहता है कि होस्टेड सिस्टम में आमतौर पर मुख्य नामक एक फ़ंक्शन होता है। लेकिन मानक में कुछ भी आपको अन्य भयावह उद्देश्यों के लिए समान पहचानकर्ता का दुरुपयोग करने से रोकता है।

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


बेवकूफ उदाहरण:

#include <stdio.h> 

int main (void) 
{ 
    int main = 5; 
    main: 

    printf("%d\n", main); 
    main--; 

    if(main) 
    { 
    goto main; 
    } 
    else 
    { 
    int main (void); 
    main(); 
    } 
} 

इस कार्यक्रम के बार-बार संख्या 5,4,3,2,1 प्रिंट होगा जब तक यह एक ढेर अतिप्रवाह और दुर्घटनाओं हो जाता है (इस घर पर कोशिश मत करो) । दुर्भाग्यवश, उपरोक्त कार्यक्रम एक कड़ाई से अनुरूप सी प्रोग्राम है और संकलक आपको इसे लिखने से नहीं रोक सकता है।

संबंधित मुद्दे