सी भाषा की कुछ विशेषताओं को हैक के रूप में शुरू किया गया जो सिर्फ काम करने के लिए हुआ।
मुख्य, साथ ही चर-लंबाई तर्क सूचियों के लिए कई हस्ताक्षर, उन विशेषताओं में से एक है।
प्रोग्रामर्स ने देखा कि वे एक फ़ंक्शन के लिए अतिरिक्त तर्क पास कर सकते हैं, और उनके दिए गए संकलक के साथ कुछ भी बुरा नहीं होता है।
यह मामला है यदि कॉलिंग कन्वेंशन ऐसे हैं:
- कॉलिंग फ़ंक्शन तर्कों को साफ़ करता है।
- बाईं ओर के तर्क स्टैक के शीर्ष, या स्टैक फ्रेम के आधार के करीब हैं, ताकि सहज तर्क पते को अमान्य न करें।
कॉलिंग सम्मेलनों का एक सेट जो इन नियमों का पालन करता है, स्टैक-आधारित पैरामीटर होता है जिससे कॉल करने वाले को तर्क मिलते हैं, और उन्हें दाएं से बाएं धक्का दिया जाता है:
;; pseudo-assembly-language
;; main(argc, argv, envp); call
push envp ;; rightmost argument
push argv ;;
push argc ;; leftmost argument ends up on top of stack
call main
pop ;; caller cleans up
pop
pop
संकलक में जहां इस तरह के कॉलिंग कन्वेंशन का मामला है, दो प्रकार के main, या यहां तक कि अतिरिक्त प्रकारों का समर्थन करने के लिए कुछ भी विशेष करने की आवश्यकता नहीं है । mainबिना किसी तर्क के एक समारोह हो सकता है, इस मामले में यह उन वस्तुओं से अनजान है जिन्हें स्टैक पर धकेल दिया गया था। यदि यह दो तर्क के एक समारोह है, तो यह पाता है argcऔर argvदो सर्वोच्च ढेर आइटम के रूप में। यदि यह एक पर्यावरण-सूचक (एक सामान्य विस्तार) के साथ एक प्लेटफ़ॉर्म-विशिष्ट तीन-तर्क संस्करण है, तो यह भी काम करेगा: यह स्टैक के शीर्ष से तीसरे तत्व के रूप में तीसरा तर्क मिलेगा।
और इसलिए एक निश्चित कॉल सभी मामलों के लिए काम करता है, जिससे एकल, निश्चित स्टार्ट-अप मॉड्यूल को कार्यक्रम से जोड़ा जा सकता है। उस मॉड्यूल को C से लिखा जा सकता है, एक फ़ंक्शन के जैसा होता है:
/* I'm adding envp to show that even a popular platform-specific variant
can be handled. */
extern int main(int argc, char **argv, char **envp);
void __start(void)
{
/* This is the real startup function for the executable.
It performs a bunch of library initialization. */
/* ... */
/* And then: */
exit(main(argc_from_somewhere, argv_from_somewhere, envp_from_somewhere));
}
दूसरे शब्दों में, यह स्टार्ट मॉड्यूल हमेशा एक तीन-तर्क मुख्य, हमेशा कहता है। यदि मुख्य कोई तर्क नहीं लेता है, या केवल int, char **, यह ठीक काम करने के लिए होता है, साथ ही साथ अगर यह कोई तर्क नहीं लेता है, तो बुलावे के कारण।
यदि आप अपने कार्यक्रम में इस तरह की बात करते हैं, तो यह आईएसओ सी द्वारा गैर-व्यवहार योग्य और अपरिभाषित व्यवहार माना जाएगा: एक तरीके से किसी फ़ंक्शन को घोषित करना और कॉल करना, और इसे दूसरे में परिभाषित करना। लेकिन एक कंपाइलर के स्टार्टअप ट्रिक का पोर्टेबल होना जरूरी नहीं है; यह पोर्टेबल कार्यक्रमों के लिए नियमों द्वारा निर्देशित नहीं है।
लेकिन मान लीजिए कि कॉलिंग कन्वेंशन ऐसे हैं जो इस तरह से काम नहीं कर सकते हैं। उस मामले में, संकलक को mainविशेष रूप से इलाज करना होगा । जब यह नोटिस करता है कि यह mainफ़ंक्शन को संकलित कर रहा है, तो यह कोड उत्पन्न कर सकता है जो तीन तर्क कॉल के साथ संगत है।
यह कहना है, आप यह लिखते हैं:
int main(void)
{
/* ... */
}
लेकिन जब कंपाइलर इसे देखता है, तो यह अनिवार्य रूप से एक कोड परिवर्तन करता है ताकि यह जो फंक्शन संकलित करता है वह इस तरह दिखाई दे:
int main(int __argc_ignore, char **__argv_ignore, char **__envp_ignore)
{
/* ... */
}
सिवाय इसके कि नाम __argc_ignoreसचमुच में मौजूद नहीं हैं। इस तरह के नामों को आपके दायरे में नहीं लाया जाता है, और अप्रयुक्त तर्कों के बारे में कोई चेतावनी नहीं होगी। कोड परिवर्तन के कारण कंपाइलर सही लिंकेज के साथ कोड का उत्सर्जन करता है जो जानता है कि उसे तीन तर्कों को साफ करना है।
__startफ़ंक्शन को कस्टम-जनरेट करने के लिए कंपाइलर या संभवतः लिंकर के लिए एक और कार्यान्वयन रणनीति है (या इसे जो भी कहा जाता है), या कम से कम कई पूर्व-संकलित विकल्पों में से एक का चयन करें। ऑब्जेक्ट फ़ाइल में जानकारी को संग्रहीत किया जा सकता है कि किस समर्थित फॉर्म का mainउपयोग किया जा रहा है। लिंकर इस जानकारी को देख सकता है, और स्टार्ट-अप मॉड्यूल के सही संस्करण का चयन कर सकता है जिसमें एक कॉल होता है mainजो प्रोग्राम की परिभाषा के अनुकूल है। सी कार्यान्वयन में आमतौर पर केवल समर्थित रूपों की एक छोटी संख्या होती है mainइसलिए यह दृष्टिकोण संभव है।
C99 भाषा के कंपाइलरों को हमेशा mainहैक का समर्थन करने के लिए, कुछ हद तक, विशेष रूप से व्यवहार करना पड़ता है कि यदि फ़ंक्शन बिना returnबयान के समाप्त हो जाता है , तो व्यवहार ऐसा है जैसे कि return 0निष्पादित किया गया हो। यह, फिर से, एक कोड परिवर्तन द्वारा इलाज किया जा सकता है। संकलक ने नोटिस किया कि एक फ़ंक्शन को mainसंकलित किया जा रहा है। फिर यह जांचता है कि क्या शरीर का अंत संभावित रूप से उपलब्ध है। यदि हां, तो यह सम्मिलित करता हैreturn 0;
mainकार्यक्रम में एक ही विधिC(या, वास्तव में, बहुत ज्यादा किसी भी भाषा में इस तरह के निर्माण के साथ) कर सकते हैं।