#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf("Ha HA see how it is?? ");
}
क्या यह अप्रत्यक्ष रूप से कॉल करता है main
? कैसे?
#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf("Ha HA see how it is?? ");
}
क्या यह अप्रत्यक्ष रूप से कॉल करता है main
? कैसे?
जवाबों:
C भाषा निष्पादन वातावरण को दो श्रेणियों में परिभाषित करती है: फ्रीस्टैंडिंग और होस्ट । दोनों निष्पादन परिवेश में प्रोग्राम स्टार्टअप के लिए पर्यावरण द्वारा एक फ़ंक्शन कहा जाता है।
एक फ्रीस्टैंडिंग पर्यावरण कार्यक्रम में स्टार्टअप फ़ंक्शन को लागू किया जा सकता है, जबकि होस्ट किए गए वातावरण में इसे परिभाषित किया जाना चाहिए main
। सी में कोई भी कार्यक्रम परिभाषित वातावरण पर प्रोग्राम स्टार्टअप फ़ंक्शन के बिना नहीं चल सकता है।
आपके मामले में, main
प्रीप्रोसेसर परिभाषाओं द्वारा छिपा हुआ है। जिसका begin()
विस्तार decode(a,n,i,m,a,t,e)
आगे तक किया जाएगा main
।
int begin() -> int decode(a,n,i,m,a,t,e)() -> int m##a##i##n() -> int main()
decode(s,t,u,m,p,e,d)
7 मापदंडों के साथ एक पैरामीटर मैक्रो है। इस मैक्रो के लिए प्रतिस्थापन सूची है m##s##u##t
। m, s, u
और प्रतिस्थापन सूची में t
4 वें , 1 सेंट , 3 आरडी और 2 एन डी पैरामीटर का उपयोग किया जाता है।
s, t, u, m, p, e, d
1 2 3 4 5 6 7
बाकी का कोई फायदा नहीं है ( सिर्फ आपत्ति करने के लिए )। तर्क दिया गया decode
" ए , एन , आई , एम , ए, टी, ई" है, इसलिए, पहचानकर्ता m, s, u
और क्रमशः t
तर्कों m, a, i
और के साथ बदल दिए जाते n
हैं।
m --> m
s --> a
u --> i
t --> n
_start()
। या इससे भी अधिक निम्न-स्तर मैं अपने कार्यक्रम की शुरुआत को उस पते से संरेखित करने का प्रयास कर सकता हूं जिसमें बूट के बाद आईपी सेट किया गया है। main()
C मानक पुस्तकालय है । सी खुद इस पर प्रतिबंध नहीं लगाता है।
decode(a,n,i,m,a,t,e)
बनते हैं m##a##i##n
? क्या यह पात्रों को प्रतिस्थापित करता है? क्या आप decode
फ़ंक्शन के प्रलेखन के लिए एक लिंक प्रदान कर सकते हैं ? धन्यवाद।
begin
परिभाषित किया गया है decode(a,n,i,m,a,t,e)
जिसके द्वारा परिभाषित किया गया है । यह फ़ंक्शन तर्कों को लेता है s,t,u,m,p,e,d
और उन्हें इस रूप में समेटता है m##s##u##t
( ##
मतलब समवर्ती)। यानी, यह पी, ई और डी के मूल्यों की उपेक्षा करता है। जैसा कि आप decode
एस = ए, टी = एन, यू = आई, एम = एम के साथ "कॉल" करते हैं, यह प्रभावी रूप से बदलता begin
है main
।
प्रयोग करके देखें gcc -E source.c
, आउटपुट समाप्त होता है:
int main()
{
printf("Ha HA see how it is?? ");
}
तो एक main()
फ़ंक्शन वास्तव में प्रीप्रोसेसर द्वारा उत्पन्न होता है।
विचाराधीन कार्यक्रम मैक्रो विस्तार के कारण कॉल करता हैmain()
, लेकिन आपकी धारणा त्रुटिपूर्ण है - इसे कॉल करने की आवश्यकता नहीं है main()
!
सख्ती से बोलना, आपके पास एक सी कार्यक्रम हो सकता है और एक main
प्रतीक के बिना इसे संकलित करने में सक्षम हो सकता है । main
यह एक ऐसी चीज है c library
, जिसमें अपनी खुद की इनिशियलाइज़ेशन को पूरा करने के बाद कूदने की उम्मीद है। आमतौर पर आप main
libc प्रतीक से कूदते हैं जिसे जाना जाता है _start
। यह हमेशा एक बहुत ही वैध कार्यक्रम संभव है, कि बस एक मुख्य होने के बिना, विधानसभा को निष्पादित करता है। इस पर एक नजर डालिए:
/* This must be compiled with the flag -nostdlib because otherwise the
* linker will complain about multiple definitions of the symbol _start
* (one here and one in glibc) and a missing reference to symbol main
* (that the libc expects to be linked against).
*/
void
_start ()
{
/* calling the write system call, with the arguments in this order:
* 1. the stdout file descriptor
* 2. the buffer we want to print (Here it's just a string literal).
* 3. the amount of bytes we want to write.
*/
asm ("int $0x80"::"a"(4), "b"(1), "c"("Hello world!\n"), "d"(13));
asm ("int $0x80"::"a"(1), "b"(0)); /* calling exit syscall, with the argument to be 0 */
}
उपरोक्त के साथ संकलित करें gcc -nostdlib without_main.c
, और इसे Hello World!
इनलाइन असेंबली में सिस्टम कॉल (इंटरप्ट) जारी करके स्क्रीन पर प्रिंट करते हुए देखें ।
इस विशेष मुद्दे के बारे में अधिक जानकारी के लिए, ksplice ब्लॉग देखें
एक और दिलचस्प मुद्दा यह है कि आपके पास एक कार्यक्रम भी हो सकता है जो main
प्रतीक के बिना एक सी फ़ंक्शन के अनुरूप है। उदाहरण के लिए, आप एक बहुत ही मान्य सी प्रोग्राम के रूप में निम्नलिखित हो सकते हैं, जब आप चेतावनी स्तर को केवल कंपाइलर व्हाइन बनाते हैं।
/* These values are extracted from the decimal representation of the instructions
* of a hello world program written in asm, that gdb provides.
*/
const int main[] = {
-443987883, 440, 113408, -1922629632,
4149, 899584, 84869120, 15544,
266023168, 1818576901, 1461743468, 1684828783,
-1017312735
};
ऐरे में मान बाइट्स हैं जो स्क्रीन पर हैलो वर्ल्ड को प्रिंट करने के लिए आवश्यक निर्देशों के अनुरूप हैं। यह विशिष्ट कार्यक्रम कैसे काम करता है, इस बारे में अधिक जानकारी के लिए, इस ब्लॉग पोस्ट पर एक नज़र डालें , जहाँ मैं इसे पहले भी पढ़ता हूँ।
मैं इन कार्यक्रमों के बारे में एक अंतिम सूचना देना चाहता हूं। मुझे नहीं पता कि वे सी भाषा विनिर्देश के अनुसार वैध सी कार्यक्रमों के रूप में पंजीकृत हैं, लेकिन उन्हें संकलित करना और उन्हें चलाना निश्चित रूप से बहुत संभव है, भले ही वे विनिर्देश का उल्लंघन करते हों।
_start
एक परिभाषित मानक के हिस्से का नाम है , या यह केवल कार्यान्वयन-विशिष्ट है? निश्चित रूप से आपका "एक सरणी के रूप में मुख्य" वास्तुकला-विशिष्ट है। यह भी महत्वपूर्ण है, सुरक्षा प्रतिबंधों के कारण रन टाइम में विफल होने के लिए यह आपके "मुख्य एक सरणी" चाल के रूप में अनुचित नहीं होगा (हालांकि यह अधिक संभावना होगी यदि आपने const
क्वालीफायर का उपयोग नहीं किया है , और अभी भी कई सिस्टम इसे अनुमति देंगे)।
_start
ELF मानक में, हालांकि AMD64 psABI का संदर्भ होता है नहीं है _start
पर 3.4 प्रक्रिया प्रारंभ । आधिकारिक तौर पर, ईएलएफ केवल e_entry
ईएलएफ हेडर में पते के बारे में जानता है , _start
केवल एक नाम है जिसे कार्यान्वयन ने चुना है।
const
एक बिट बात नहीं होगी - उस बाइनरी निष्पादन योग्य फ़ाइल में प्रतीक का नाम है main
। न आधिक न कम। const
एक सी निर्माण है जिसका अर्थ है निष्पादन समय पर कुछ भी नहीं।
कोई जादूगर की तरह काम करने की कोशिश कर रहा है। वह सोचता है कि वह हमें बरगला सकता है। लेकिन हम सभी जानते हैं, सी कार्यक्रम निष्पादन के साथ शुरू होता है main()
।
int begin()
साथ प्रतिस्थापित किया जाएगा decode(a,n,i,m,a,t,e)
पूर्वप्रक्रमक मंच पर एक पास से। फिर फिर से, decode(a,n,i,m,a,t,e)
एम ## ए ## मैं ## एन के साथ बदल दिया जाएगा। मैक्रो कॉल की स्थिति के अनुसार, s
चरित्र का एक मूल्य होगा a
। इसी तरह, u
'i' t
से बदलकर 'n' से बदल दिया जाएगा। और, यह कैसे है, m##s##u##t
बन जाएगाmain
##
मैक्रो विस्तार में प्रतीक के बारे में, यह प्रीप्रोसेसिंग ऑपरेटर है और यह टोकन चिपकाने का काम करता है। जब एक मैक्रो का विस्तार किया जाता है, तो प्रत्येक '##' ऑपरेटर के दोनों ओर के दो टोकन एक एकल टोकन में जुड़ जाते हैं, जो फिर '##' और दो मूल टोकन को मैक्रो विस्तार में बदल देता है।
यदि आपको मुझ पर विश्वास नहीं है, तो आप -E
ध्वज के साथ अपना कोड संकलित कर सकते हैं । यह प्रीप्रोसेसिंग के बाद संकलन प्रक्रिया को रोक देगा और आप टोकन चिपकाने का परिणाम देख सकते हैं।
gcc -E FILENAME.c
decode(a,b,c,d,[...])
पहले चार तर्कों को फेरबदल करता है और क्रम में एक नया पहचानकर्ता प्राप्त करने के लिए उन्हें जोड़ता है dacb
। (शेष तीन तर्कों की अनदेखी की जाती है।) उदाहरण के लिए, decode(a,n,i,m,[...])
पहचानकर्ता देता है main
। ध्यान दें कि यह वही है जिसे begin
मैक्रो के रूप में परिभाषित किया गया है।
इसलिए, begin
मैक्रो को बस के रूप में परिभाषित किया गया है main
।
आपके उदाहरण में, main()
फ़ंक्शन वास्तव में मौजूद है, क्योंकि begin
एक मैक्रो है जो कंपाइलर decode
मैक्रो के साथ बदलता है जो बदले में अभिव्यक्ति m ## s ## u ## t द्वारा प्रतिस्थापित किया जाता है। मैक्रो विस्तार का उपयोग करके ##
, आप शब्द main
से पहुंचेंगे decode
। यह एक ट्रेस है:
begin --> decode(a,n,i,m,a,t,e) --> m##parameter1##parameter3##parameter2 ---> main
यह सिर्फ करने के लिए एक चाल है main()
, लेकिन main()
प्रोग्राम की प्रविष्टि फ़ंक्शन के लिए नाम का उपयोग करना सी प्रोग्रामिंग भाषा में आवश्यक नहीं है। यह आपके ऑपरेटिंग सिस्टम और लिंकर पर निर्भर करता है।
Windows में, आप हमेशा उपयोग नहीं करते main()
हैं, लेकिन बल्कि WinMain
याwWinMain
, हालांकि आप उपयोग कर सकते हैं main()
, यहां तक कि माइक्रोसॉफ्ट के toolchain के साथ । लिनक्स में, कोई भी उपयोग कर सकता है _start
।
यह प्रवेश बिंदु को सेट करने के लिए एक ऑपरेटिंग सिस्टम टूल के रूप में लिंकर पर निर्भर है, और भाषा ही नहीं। तुम भी हमारे प्रवेश बिंदु सेट कर सकते हैं, और आप एक पुस्तकालय बना सकते हैं जो निष्पादन योग्य भी है !
main()
दिया है, जो C प्रोग्रामिंग लैंग्वेज के फंक्शन को बांधता है , जो कि सही नहीं है।