मुख्य कार्य के बजाय वैश्विक चर वाले कार्यक्रम को मुख्य कैसे कहा जा सकता है?


97

निम्नलिखित कार्यक्रम पर विचार करें:

#include <iostream>
int main = ( std::cout << "C++ is excellent!\n", 195 ); 

Windows 7 OS पर g ++ 4.8.1 (mingw64) का उपयोग करना, प्रोग्राम संकलित करता है और ठीक चलता है, मुद्रण:

C ++ बहुत बढ़िया है!

सांत्वना देने के लिए। mainएक फ़ंक्शन के बजाय एक वैश्विक चर प्रतीत होता है; यह प्रोग्राम बिना फंक्शन के कैसे चल सकता है main()? क्या यह कोड C ++ मानक के अनुरूप है? क्या कार्यक्रम का व्यवहार अच्छी तरह से परिभाषित है? मैंने भी -pedantic-errorsविकल्प का उपयोग किया है लेकिन कार्यक्रम अभी भी संकलित है और चलता है।


11
@ isν necessaryαῥεῖ: भाषा वकील टैग क्यों आवश्यक है?
विध्वंसक

14
ध्यान दें कि निर्देश के 195लिए ओपकोड है RET, और सी कॉलिंग कन्वेंशन में, कॉलर स्टैक को साफ करता है।
ब्रायन

2
@PravasiMeet "तो यह कार्यक्रम कैसे कार्यान्वित होता है" - क्या आपको नहीं लगता कि एक चर के लिए आरंभीकरण कोड को निष्पादित किया जाना चाहिए (यहां तक ​​कि main()फ़ंक्शन के बिना भी ; वास्तव में, वे पूरी तरह से असंबंधित हैं।)
पैरामैग्नेटिक क्रोइसैन कोड

4
मैं उन लोगों में शामिल हूं, जिन्होंने पाया कि कार्यक्रम (64-बिट linux, g ++ 5.1 / clang 3.6) के रूप में segfaults है। मैं इसे int main = ( std::cout << "C++ is excellent!\n", exit(0),1 );(और सहित <cstdlib>) में संशोधन करके इसे सुधार सकता हूं , हालांकि यह कार्यक्रम कानूनी रूप से बीमार बना हुआ है।
माइक किंगहान

11
@ ब्रायन आपको उस तरह के बयान देते समय वास्तुकला का उल्लेख करना चाहिए। सारी दुनिया एक वैक्स नहीं है। या x86। जो कुछ भी।
dmckee --- पूर्व-मध्यस्थ ने बिल्ली का बच्चा

जवाबों:


85

क्या चल रहा है, इस बारे में प्रश्न के मांस में जाने से पहले, यह बताना महत्वपूर्ण है कि दोष रिपोर्ट 1886 के अनुसार कार्यक्रम बीमार है : मुख्य के लिए भाषा लिंकेज () :

[...] एक प्रोग्राम जो वैश्विक दायरे में एक चर मुख्य घोषित करता है या जो सी भाषा लिंकेज (किसी भी नामस्थान में) के साथ मुख्य नाम घोषित करता है, बीमार है। [...]

क्लैंग और जीसीसी के सबसे हाल के संस्करण इसे एक त्रुटि बनाते हैं और कार्यक्रम संकलित नहीं करेगा ( जीसीसी लाइव उदाहरण देखें ):

error: cannot declare '::main' to be a global variable
int main = ( std::cout << "C++ is excellent!\n", 195 ); 
    ^

तो gcc और clang के पुराने संस्करणों में कोई डायग्नोस्टिक क्यों नहीं था? इस दोष रिपोर्ट में 2014 के अंत तक एक प्रस्तावित प्रस्ताव भी नहीं था और इसलिए यह मामला केवल हाल ही में स्पष्ट रूप से बीमार बना हुआ था, जिसे निदान की आवश्यकता है।

इससे पहले, ऐसा लगता है जैसे इस अपरिभाषित व्यवहार किया जाएगा, क्योंकि हम एक का उल्लंघन कर रहे करेगा मसौदा सी की आवश्यकता ++ अनुभाग से मानक 3.6.1 [basic.start.main] :

एक कार्यक्रम में मुख्य नामक एक वैश्विक फ़ंक्शन शामिल होगा, जो कार्यक्रम की निर्दिष्ट शुरुआत है। [...]

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

तो वास्तव में कोड क्या कर रहा है और कुछ मामलों में यह परिणाम क्यों देता है? आइए देखें कि हमारे पास क्या है:

declarator  
|        initializer----------------------------------
|        |                                           |
v        v                                           v
int main = ( std::cout << "C++ is excellent!\n", 195 ); 
    ^      ^                                   ^
    |      |                                   |
    |      |                                   comma operator
    |      primary expression
global variable of type int

हम mainजो एक है पूर्णांक ग्लोबल नेम स्पेस में घोषित कर दिया और प्रारंभ की जा रही है, चर स्थिर भंडारण अवधि है। यह कार्यान्वित किया गया है कि क्या कॉल करने के प्रयास से पहले इनिशियलाइज़ेशन हो जाएगा mainलेकिन ऐसा लगता है कि कॉल करने से पहले ऐसा होता है main

कोड अल्पविराम ऑपरेटर का उपयोग करता है , बाएं ऑपरेंड एक त्याग मूल्य अभिव्यक्ति है और कॉलिंग के साइड इफेक्ट के लिए पूरी तरह से यहां उपयोग किया जाता है std::cout। अल्पविराम ऑपरेटर का परिणाम सही ऑपरेंड है जो इस मामले में प्रचलन है 195जो चर को सौंपा गया है main

हम सार्जेंट पॉइंट्स को असेंबली असेंबली शो के coutदौरान देख सकते हैं जिसे स्टैटिक इनिशियलाइज़ेशन के दौरान कहा जाता है। हालाँकि चर्चा के लिए और अधिक दिलचस्प बिंदु लाइव गॉडबोल्ट सत्र यह होगा:

main:
.zero   4

और बाद में:

movl    $195, main(%rip)

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

ऐसा प्रतीत होता है कि हम सी का उपयोग करके इसे प्राप्त कर सकते हैं ( इसे लाइव देखें ):

const int main = 195 ;

यदि यह वस्तुतः चर mainनहीं है तो यह सह-दोष है क्योंकि यह एक निष्पादन योग्य स्थान पर स्थित नहीं है, यहां टिप टिप इस टिप्पणी के लिए है जिसने मुझे यह विचार दिया।

इस प्रश्न के C विशिष्ट संस्करण के लिए यहां भी FUZxxl उत्तर देखें ।


कार्यान्वयन कोई चेतावनी भी क्यों नहीं दे रहा है। (जब मैं उपयोग करता हूँ -वैल और -वैक्रा यह अभी भी एकल चेतावनी नहीं देता है)। क्यों? क्या आप इस सवाल का @Mark बी के जवाब के बारे में सोचते हैं?
विध्वंसक

IMHO, संकलक को चेतावनी नहीं देनी चाहिए क्योंकि mainआरक्षित पहचानकर्ता नहीं है (3.6.1 / 3)। इस मामले में, मुझे लगता है कि VS2013 का इस मामले से निपटना (देखें फ्रांसिस कुगलर का जवाब) यह gcc और क्लैंग की तुलना में अधिक सही है।
cdmh

@PravasiMeet मैंने अपने उत्तर wrt को अपडेट किया कि gcc के पूर्व संस्करणों ने डायग्नोस्टिक क्यों नहीं दिया।
शफीक यघमौर

2
... और वास्तव में, जब मैं ओपी के प्रोग्राम को लिनक्स / x86-64 पर परीक्षण करता हूं, जी ++ 5.2 के साथ (जो प्रोग्राम को स्वीकार करता है - मुझे लगता है कि आप "सबसे हाल के संस्करण" के बारे में मजाक नहीं कर रहे थे), यह ठीक उसी जगह पर दुर्घटनाग्रस्त हो जाता है जहां मुझे इसकी उम्मीद थी चाहेंगे।
zwol

1
@Walter मुझे विश्वास नहीं है कि ये डुप्लिकेट हैं पूर्व में बहुत संकीर्ण सवाल पूछ रहा है। एसओ उपयोगकर्ताओं का स्पष्ट रूप से एक समूह है जो डुप्लिकेट का एक अधिक कटौतीवादी दृष्टिकोण है जो मुझे बहुत ज्यादा समझ में नहीं आता है क्योंकि हम पुराने प्रश्नों के कुछ संस्करण के लिए सबसे अधिक एसओ प्रश्नों को उबाल सकते हैं तो एसओ बहुत उपयोगी नहीं होगा।
शाफिक याघमोर

20

3.6.1 / 1 से:

एक कार्यक्रम में मुख्य नामक एक वैश्विक फ़ंक्शन शामिल होगा, जो कार्यक्रम की निर्दिष्ट शुरुआत है। यह कार्यान्वयन को परिभाषित करता है कि क्या मुख्य कार्य को परिभाषित करने के लिए एक मुक्त वातावरण में एक कार्यक्रम की आवश्यकता है।

इससे ऐसा लगता है कि जी ++ एक कार्यक्रम (मुख्य रूप से "फ्रीस्टैंडिंग" क्लॉज) के रूप में एक मुख्य फ़ंक्शन के बिना अनुमति देता है।

फिर 3.6.1 / 3 से:

एक कार्यक्रम के भीतर फ़ंक्शन मेन का उपयोग नहीं किया जाएगा (3.2)। मुख्य रूप से लिंकेज (3.5) कार्यान्वयन परिभाषित है। एक प्रोग्राम जो मुख्य को इनलाइन या स्टेटिक घोषित करता है, वह विकृत है। मुख्य नाम अन्यथा आरक्षित नहीं है।

तो यहाँ हम सीखते हैं कि एक पूर्णांक चर नाम दिया जाना पूरी तरह से ठीक है main

अंत में अगर आप सोच रहे हैं कि आउटपुट क्यों प्रिंट किया जाता है, तो स्थैतिक init पर int mainअमल करने के लिए अल्पविराम ऑपरेटर का उपयोग प्रारंभ होता है coutऔर फिर प्रारंभ करने के लिए एक वास्तविक अभिन्न मूल्य प्रदान करता है।


7
यह ध्यान रखना दिलचस्प है कि यदि आप mainकिसी और चीज़ का नाम लेते हैं तो लिंकिंग विफल हो जाती है: (.text+0x20): undefined reference to मुख्य ``
फ्रेड लार्सन

1
क्या आपको यह निर्दिष्ट करने की आवश्यकता नहीं है कि आपका कार्यक्रम फ्रीस्टैंडिंग है?
शफीक यघमौर

9

gcc 4.8.1 निम्नलिखित x86 विधानसभा उत्पन्न करता है:

.LC0:
    .string "C++ is excellent!\n"
    subq    $8, %rsp    #,
    movl    std::__ioinit, %edi #,
    call    std::ios_base::Init::Init() #
    movl    $__dso_handle, %edx #,
    movl    std::__ioinit, %esi #,
    movl    std::ios_base::Init::~Init(), %edi  #,
    call    __cxa_atexit    #
    movl    $.LC0, %esi #,
    movl    std::cout, %edi #,
    call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)   #
    movl    $195, main(%rip)    #, main
    addq    $8, %rsp    #,
    ret
main:
    .zero   4

ध्यान दें कि coutआरंभीकरण के दौरान कहा जाता है, mainफ़ंक्शन में नहीं !

.zero 44 (0-इनिशियलाइज़्ड) बाइट्स को स्थान पर शुरू करने की घोषणा करता है main, जहां चरmain का नाम है !]

mainप्रतीक कार्यक्रम की शुरुआत के रूप में व्याख्या की है। व्यवहार मंच पर निर्भर करता है।


1
नोट के रूप में ब्रायन बताते हैं 195 कि retकुछ आर्किटेक्चर के लिए opcode है । इसलिए शून्य निर्देश सही नहीं हो सकता है।
शफीक यघमौर

@ शफीक्यगहमौर आपकी टिप्पणी के लिए धन्यवाद, आप सही हैं। मैं कोडांतरक निर्देशों के साथ गड़बड़ हो गया।
सर्ज

8

यह एक बीमार कार्यक्रम है। यह मेरे परीक्षण वातावरण, साइबरविन / जी ++ 4.9.3 पर क्रैश हो जाता है।

मानक से:

3.6.1 मुख्य कार्य [basic.start.main]

1 एक कार्यक्रम में मुख्य नामक एक वैश्विक फ़ंक्शन शामिल होगा, जो कार्यक्रम की निर्दिष्ट शुरुआत है।


मुझे लगता है कि मेरे द्वारा उद्धृत दोष रिपोर्ट से पहले, यह सिर्फ सादा अपरिभाषित व्यवहार था।
शफीक यघमौर

@ShafikYaghmour, कि सामान्य सिद्धांत सभी स्थानों पर जहां मानक का उपयोग करता है पर लागू किया जाना है करेगा ?
आर साहू

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

3
C99 खंड 4 ("अनुरूपता") यह असंदिग्ध बनाता है: "यदि एक '' 'या' की आवश्यकता नहीं होगी 'जो एक बाधा के बाहर प्रकट होती है, तो उल्लंघन किया जाता है, व्यवहार अपरिभाषित है।" मुझे C ++ 98 या C ++ 11 में समतुल्य शब्द नहीं मिल रहे हैं, लेकिन मुझे दृढ़ता से संदेह है कि समिति का यह मतलब है कि यह वहां होगा। (सी और सी ++ समितियों को वास्तव में नीचे बैठने की ज़रूरत है और दो मानकों के बीच के सभी पारिभाषिक अंतरों को दूर करना होगा।)
zwol

7

मेरा मानना ​​है कि यह काम करता है कि संकलक को पता नहीं है कि यह main()फ़ंक्शन को संकलित कर रहा है, इसलिए यह असाइनमेंट साइड-इफेक्ट के साथ एक वैश्विक पूर्णांक संकलित करता है।

इस ट्रांसलेशन-यूनिट को जिस ऑब्जेक्ट फॉर्मेट में संकलित किया गया है, वह फंक्शन सिंबल और वेरिएबल सिंबल के बीच अंतर करने में सक्षम नहीं है ।

तो लिंकर खुशी से (चर) मुख्य प्रतीक से जोड़ता है और इसे फ़ंक्शन कॉल की तरह मानता है। लेकिन तब तक नहीं जब तक कि रनटाइम सिस्टम ने वैश्विक वैरिएबल इनिशियलाइज़ेशन कोड नहीं चलाया।

जब मैंने सैंपल चलाया तो इसका प्रिंट आउट निकल गया, लेकिन तब इसने सीग-फॉल्ट का कारण बना । मुझे लगता है कि जब रनटाइम सिस्टम ने एक अंतर चर को निष्पादित करने की कोशिश की, जैसे कि यह एक फ़ंक्शन था


4

मैंने VS2013 का उपयोग करके Win7 64bit OS पर यह कोशिश की है और यह सही ढंग से संकलित करता है, लेकिन जब मैं एप्लिकेशन बनाने का प्रयास करता हूं तो मुझे आउटपुट विंडो से यह संदेश मिलता है।

1>------ Build started: Project: tempTest, Configuration: Debug Win32 ------
1>LINK : fatal error LNK1561: entry point must be defined
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

2
एफडब्ल्यूआईडब्ल्यू, यह एक लिंकर त्रुटि है, डिबगर से संदेश नहीं। संकलन सफल हुआ, लेकिन लिंकर एक फ़ंक्शन नहीं ढूंढ सका main()क्योंकि यह प्रकार का एक चर हैint
cdmh

उत्तर के लिए धन्यवाद, मैं इसे दर्शाने के लिए अपना प्रारंभिक उत्तर दूंगा।
फ्रांसिस कुगलर

-1

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.