printf जैसे वैरिएडिक फ़ंक्शंस उनके द्वारा प्राप्त तर्कों की संख्या का पता कैसे लगा सकते हैं?

तर्कों की मात्रा स्पष्ट रूप से एक (छिपे हुए) पैरामीटर के रूप में पारित नहीं होती है (देखें एएसएम उदाहरण में प्रिंटफ को यहां कॉल करें)।

चाल क्या है?

13
masterxilo 11 मार्च 2011, 15:08

4 जवाब

सबसे बढ़िया उत्तर

चाल यह है कि आप उन्हें किसी और तरह से बताते हैं। printf के लिए आपको एक प्रारूप स्ट्रिंग की आपूर्ति करनी होगी जिसमें प्रकार की जानकारी भी हो (जो कि गलत हो सकती है)। इस जानकारी की आपूर्ति करने का तरीका मुख्य रूप से उपयोगकर्ता-अनुबंध और अक्सर त्रुटि-प्रवण होता है।

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

संपादित करें: सी ++ 0x में विविध कार्यों को कॉल करने के लिए एक सुरक्षित तरीका (यहां तक ​​​​कि टाइपएफ़!)

13
coldfix 11 मार्च 2011, 15:20

स्पष्ट रूप से, प्रारूप स्ट्रिंग से। ध्यान दें कि पारित किए गए तर्कों की कुल "चर" संख्या को पुनः प्राप्त करने के लिए stdarg.h में कोई मैक्रोज़ नहीं है। यह भी एक कारण है कि सी कॉलिंग कन्वेंशन के लिए कॉलर को स्टैक को साफ करने की आवश्यकता होती है, भले ही यह कोड आकार को बढ़ाता है।

9
Ruud Koot 11 मार्च 2011, 19:07

यही कारण है कि सी कॉलिंग कन्वेंशन पर रिवर्स ऑर्डर पर तर्कों को धक्का दिया जाता है, जैसे:

यदि आप कॉल करते हैं:

printf("%s %s", foo, bar);

ढेर की तरह समाप्त होता है:

  ...
+-------------------+
| bar               |
+-------------------+
| foo               |
+-------------------+
| "%s %s"           |
+-------------------+
| return address    |
+-------------------+
| old frame pointer | <- frame pointer
+-------------------+
  ...

तर्कों को परोक्ष रूप से फ्रेम पॉइंटर से ऑफसेट का उपयोग करके एक्सेस किया जाता है (फ्रेम पॉइंटर को स्मार्ट कंपाइलर्स द्वारा छोड़ा जा सकता है जो स्टैक पॉइंटर से चीजों की गणना करना जानते हैं)। इस योजना में पहला तर्क हमेशा एक प्रसिद्ध पते पर होता है, फ़ंक्शन उतने तर्कों तक पहुँचता है जितना कि इसके पहले तर्क इसे बताते हैं।

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

printf("%x %x %x %x %x %x\n");

यह ढेर के हिस्से को डंप कर देगा।

9
ninjalj 14 मार्च 2011, 03:29
  • AMD64 सिस्टम V ABI(लिनक्स, मैक ओएस एक्स) पास नहीं होता है किसी भी मानक IA-32 कॉलिंग सम्मेलनों के विपरीत, al (RAX की निम्न बाइट) में संख्या वेक्टर (SSE / AVX) भिन्न होता है। यह भी देखें: प्रिंटफ को कॉल करने से पहले %eax को शून्य क्यों किया जाता है?

    लेकिन केवल 8 तक (उपयोग करने के लिए रजिस्टरों की अधिकतम संख्या)। और IIRC, ABI al को XMM/YMM/ZMM args की वास्तविक संख्या से अधिक होने की अनुमति देता है लेकिन यह कम नहीं होना चाहिए। तो यह सामान्य रूप से आपको हमेशा FP args की संख्या नहीं बताता है; आप यह नहीं बता सकते कि 8 से अधिक कितने हैं, और al को अधिक गणना करने की अनुमति है।

    यह केवल प्रदर्शन कारणों के लिए प्रयोग करने योग्य है, अनावश्यक वेक्टर रजिस्टरों को "3.5.7 परिवर्तनीय तर्क सूची" में उल्लिखित "रजिस्टर सेव एरिया" में सहेजना छोड़ना है। उदाहरण के लिए GCC कोड बनाता है जो al!=0 का परीक्षण करता है और फिर XMM0..7 को स्टैक या कुछ भी नहीं डंप करता है। (या यदि फ़ंक्शन VA_ARG __m256 के साथ कहीं भी उपयोग करता है, तो YMM0..7।)

  • सी स्तर पर, प्रारूप स्ट्रिंग को पार्स करने के अलावा अन्य तकनीकें भी हैं जैसा कि दूसरों ने बताया है। आप यह भी कर सकते हैं:

    • अंतिम तर्क जैसे निष्पादित करता है।

      आप sentinel फ़ंक्शन विशेषता का उपयोग करना चाहेंगे ताकि GCC को संकलन समय पर इसे लागू करने में मदद मिल सके: सी चेतावनी फंक्शन कॉल में सेंटीनेल गुम होना

    • इसे varargs की संख्या के साथ एक अतिरिक्त पूर्णांक तर्क के रूप में पास करें

    • format फ़ंक्शन विशेषता का उपयोग करके GCC को printf या strftime जैसे ज्ञात प्रकारों के प्रारूप स्ट्रिंग को लागू करने में सहायता करें

संबंधित: gcc में वेरिएबल तर्कों को कैसे लागू किया जाता है?

5
Peter Cordes 30 अगस्त 2019, 10:25