2012-09-11 11 views
17
int max(int n, ...) 

मैं cdecl कॉलिंग सम्मेलन का उपयोग कर रहा हूं जहां कॉलर कैली रिटर्न के बाद चर को साफ़ करता है।जीसीसी में परिवर्तनीय तर्क कैसे लागू किए गए हैं?

मुझे यह जानने में दिलचस्पी है कि मैक्रोज़ va_end, va_start और va_arg काम कैसे करते हैं?

क्या कॉलर अधिकतम तर्क के रूप में तर्क के सरणी के पते में पास करता है?

उत्तर

22

आप जिस तरह से सी भाषा स्टैक पर मानकों को संग्रहीत करता है को देखें, तो जिस तरह से मैक्रो काम स्पष्ट हो जाना चाहिए: -

Higher memory address Last parameter 
         Penultimate parameter 
         .... 
         Second parameter 
Lower memory address  First parameter 
     StackPointer -> Return address 

(ध्यान दें, हार्डवेयर ढेर सूचक पर निर्भर करता है शायद एक पंक्ति नीचे और उच्च और निचला जा सकता है)

तर्क हमेशा की तरह संग्रहीत किए जाते हैं, यहां तक ​​कि ... पैरामीटर प्रकार के बिना भी।

va_start मैक्रो बस एक सूचक पहले समारोह पैरामीटर के लिए, e.g.:-

void func (int a, ...) 
{ 
    // va_start 
    char *p = (char *) &a + sizeof a; 
} 

जो दूसरा पैरामीटर को p बिंदु बनाता है निर्धारित करता है।va_arg मैक्रो करता है: -

void func (int a, ...) 
{ 
    // va_start 
    char *p = (char *) &a + sizeof a; 

    // va_arg 
    int i1 = *((int *)p); 
    p += sizeof (int); 

    // va_arg 
    int i2 = *((int *)p); 
    p += sizeof (int); 

    // va_arg 
    long i2 = *((long *)p); 
    p += sizeof (long); 
} 

va_end मैक्रो सिर्फ NULL करने के लिए p मूल्य तय करता है।

नोट:

  1. अनुकूलन संकलनकर्ता और रजिस्टरों में कुछ RISC सीपीयू दुकान मानकों के बजाय ढेर का उपयोग करें। ... पैरामीटर की उपस्थिति इस क्षमता को बंद कर देगी और संकलक के लिए स्टैक का उपयोग करने के लिए।
+10

यह वास्तव में काफी मंच-विशिष्ट है, क्योंकि * कई * कॉलिंग सम्मेलन (सामान्य x64, पीपीसी, एआरएम समेत) रजिस्ट्रारों में अपने अधिकांश पैरामीटर पास करते हैं। कई प्लेटफार्म स्टैक पर रिटर्न पता नहीं डालते हैं, एक या दो प्लेटफार्मों में ढेर होते हैं जो नीचे की ओर ऊपर की ओर बढ़ते हैं, और कुछ कॉलिंग सम्मेलन विपरीत क्रम में स्टैक पर तर्क डालते हैं। –

+0

@ स्कीज़: बहुत बढ़िया जवाब !! – Bruce

+0

@DietrichEpp: मुझे पता है। लेकिन उम्मीद है कि यह कुछ मूलभूत बातें प्राप्त करता है। मैंने स्टैक के विभिन्न तरीकों को प्रतिबिंबित करने के जवाब में कुछ नोट्स दिए हैं। फिर भी, संकलक लागू करने के कई अलग-अलग तरीकों को कवर करने में इसका अधिक लंबा जवाब लगेगा। सरल तरीका मैक्रो परिभाषाओं को ढूंढना होगा और देखें कि वे विस्तार करते हैं और उम्मीद है कि कोई डरावना कंपाइलर जादू नहीं चल रहा है। – Skizz

8

चूंकि स्टैक पर तर्क पारित किए जाते हैं, va_ "फ़ंक्शंस" (वे मैक्रोज़ के रूप में लागू होने वाले अधिकांश समय होते हैं) बस एक निजी स्टैक पॉइंटर का उपयोग करते हैं। यह निजी स्टैक पॉइंटर va_start पर दिए गए तर्क से संग्रहीत है, और फिर va_arg "स्टैक" से तर्कों को "पॉप" करता है क्योंकि यह पैरामीटर को पुन: सक्रिय करता है।

चलें कहते हैं कि तुम तीन मापदंडों के साथ समारोह max कहते हैं, इस तरह:

max(a, b, c); 

max समारोह के अंदर, ढेर मूल रूप से इस तरह दिखता है:

 
     +-----+ 
     | c | 
     | b | 
     | a | 
     | ret | 
SP -> +-----+ 

SP असली ढेर सूचक है , और यह वास्तव में a, b और c नहीं है जो ढेर पर उनके मूल्य हैं। ret वापसी का पता है, जहां कार्य पूरा होने पर कूदना है।

क्या va_start(ap, n) करता है (अपने समारोह प्रोटोटाइप में n) तर्क का पता लेने के लिए और है कि अगले तर्क की स्थिति की गणना करता है से है, इसलिए हम एक नए निजी ढेर सूचक मिलती है:

 
     +-----+ 
     | c | 
ap -> | b | 
     | a | 
     | ret | 
SP -> +-----+ 

जब आप va_arg(ap, int) का उपयोग करें जो निजी स्टैक पॉइंटर को इंगित करता है, और उसके बाद अगली तर्क पर इंगित करने के लिए निजी स्टैक पॉइंटर को बदलकर इसे "पॉप" करता है। ढेर अब इस तरह दिखते हैं:

 
     +-----+ 
ap -> | c | 
     | b | 
     | a | 
     | ret | 
SP -> +-----+ 

यह विवरण निश्चित रूप से सरल है, लेकिन सिद्धांत दिखाता है।

+0

निश्चित रूप से 'va_arg' कॉलर-क्लीनअप सम्मेलन में बैठे नहीं तो स्टैक से इसे पॉप नहीं कर सकता है। – Puppy

+0

@ जोचिम: क्या आप कुछ उदाहरण दे सकते हैं या वर्णन कर सकते हैं कि आप अधिक विस्तार से जवाब दें। मैं कल्पना नहीं कर सकता कि आप क्या कह रहे हैं। – Bruce

+0

@DeadMG बेशक यह नहीं है, इसलिए मैंने कोटेशन के अंदर पॉप डाला। :) –

0
int max(int n, const char *msg,...) 
{ 
va_list args; 
char buffer[1024]; 
va_start(args, msg); 
nb_char_written = vsnprintf(buffer, 1024, msg, args); 
va_end(args); 
printf("(%d):%s\n",n,buffer); 
} 
+0

आपके उत्तर के लिए धन्यवाद। मुझे यह जानने में अधिक दिलचस्पी है कि कैली के लिए स्टैक कैसे स्थापित किया जाता है (कैसे धक्का दिया जाता है) और मैक्रोज़ कैसे काम करते हैं? – Bruce

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