2010-08-26 12 views
26

किसी भी सी प्रोग्रामर है जो एक सप्ताह से अधिक के लिए काम कर रहा वास्तविक बहस की तुलना में अधिक प्रारूप विनिर्देशक साथ printf कॉल करने से दुर्घटनाओं कि परिणाम का सामना किया है, जैसे:भी कई तर्क पासिंग printf को

printf("Gonna %s and %s, %s!", "crash", "burn"); 

हालांकि, कर रहे हैं वहाँ किसी भी ऐसी ही बुरी चीजें जो तब हो सकती हैं जब आप पास करते हैं printf के लिए तर्क?

printf("Gonna %s and %s!", "crash", "burn", "dude"); 

86/64 विधानसभा का मेरा ज्ञान, मुझे विश्वास है कि यह हानिरहित है, हालांकि मुझे विश्वास है वहाँ कुछ बढ़त हालत मैं याद कर रहा हूँ नहीं है कि नहीं कर रहा हूँ, और मैं अन्य आर्किटेक्चर के बारे में पता नहीं है। क्या यह स्थिति हानिरहित होने की गारंटी है, या यहां पर संभावित रूप से क्रैश-प्रेरक पतन भी है?

+3

नहीं अपने प्रश्न का उत्तर, स्टेकर के सही है, लेकिन दुर्घटनाओं के लिए। 'gcc' को उस पर अच्छी चेतावनियां देनी चाहिए, इसलिए उसको अनदेखा करने के लिए वास्तव में कोई बहाना नहीं है ;-) –

+0

जीसीसी इसके लिए चेतावनी कैसे दे सकता है? ध्यान दें कि प्रारूप स्ट्रिंग को लगातार स्ट्रिंग नहीं होना चाहिए। यह कोई 'char *' –

+1

हो सकता है जीसीसी संकलन समय पर प्रारूप स्ट्रिंग को जान सकता है जब अच्छी चेतावनी दे सकता है। चूंकि यह 'printf' और दोस्तों के लिए तर्कसंगत उपयोग के मामलों के एक बड़े स्वार्थ का प्रतिनिधित्व करता है, इसलिए वे चेतावनियां मूल्यवान हैं और उन्हें ध्यान दिया जाना चाहिए। – RBerteig

उत्तर

12

आप शायद कुछ इस तरह के रूप में printf समारोह के लिए प्रोटोटाइप पता

int printf(const char *format, ...); 

इस बात का एक और पूरी संस्करण वास्तव में

int __cdecl printf(const char *format, ...); 

__cdecl "बुला सम्मेलन" को परिभाषित करता होगा जो, अन्य चीजों के साथ, वर्णन करता है कि तर्क कैसे प्रबंधित किए जाते हैं। इस मामले में इसका मतलब है कि तर्क स्टैक पर धकेल दिए जाते हैं और कॉल को कॉल करने के द्वारा स्टैक साफ़ किया जाता है।

_cdecl का एक विकल्प __stdcall है, अन्य भी हैं। __stdcall के साथ सम्मेलन यह है कि तर्क स्टैक पर धकेल दिए जाते हैं और जिसे फ़ंक्शन द्वारा साफ़ किया जाता है। हालांकि, जहां तक ​​मुझे पता है, __stdcall फ़ंक्शन के लिए तर्कों की एक चर संख्या को स्वीकार करने के लिए संभव नहीं है। यह समझ में आता है क्योंकि यह नहीं जानता कि कितना ढेर साफ करना है।

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

कॉलिंग सम्मेलनों पर अधिक जानकारी विकिपीडिया here पर पाई जा सकती है।

+6

__cdecl एक Win32ism है, इस तथ्य से बनाया गया है कि कुछ पुराने डॉस कंपाइलर्स सी और पास्कल कॉलिंग सम्मेलनों दोनों का समर्थन करते हैं। – ninjalj

+1

@ निंजलज, '__cdecl' केवल एमएस कंपाइलर्स द्वारा समर्थित है, लेकिन सम्मेलन कॉल करने के बारे में सामान्य नोट सभी ओएस के लिए मान्य है। –

+0

@ जेएसबीएंग्स: __cdecl को बोर्लैंड कंपाइलर्स आईआईआरसी द्वारा भी समर्थित किया गया था। इसके अलावा, अधिकांश अन्य ओएसई सी पर केवल सी कॉलिंग कन्वेंशन (दाएं से बाएं, कॉलर क्लींस क्लीन्स) का उपयोग करता है, संभवतः आईएसआर के लिए वेरिएंट, अन्य कंपाइलर्स के साथ संगतता, और/या रजिस्टरों (जीसीसी के regparm) पर तर्कों को सहेजना। AFAIK, Win32 एकमात्र मंच है जहां आप एक कॉलिंग सम्मेलन का चयन कर सकते हैं जो vararg कार्यों का समर्थन नहीं करता है। – ninjalj

3

सभी तर्क स्टैक पर धकेल दिए जाएंगे और स्टैक फ्रेम हटा दिए जाने पर हटा दिया जाएगा। यह व्यवहार एक विशिष्ट प्रोसेसर से स्वतंत्र है। (मुझे केवल एक मेनफ्रेम याद है जिसमें 70 के दशक में डिज़ाइन नहीं किया गया था) तो, हाँ दूसरा उदाहरण असफल नहीं होगा।

3

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

+0

इसमें जोड़ने के लिए, आपका कंपाइलर अतिरिक्त पैरामीटर को भी खत्म कर सकता है अगर यह पता लगा सकता है कि वे अप्रयुक्त हैं। आपको असेंबली आउटपुट को यह बताने के लिए देखना होगा कि क्या अतिरिक्त पैरा वास्तव में 'printf' को पास कर दिए गए हैं या यदि वे ऑप्टिमाइज़ हो जाते हैं। – bta

+0

यदि संकलक यह सुनिश्चित करने के लिए जानता है कि यह प्रिंटफ की प्रारूप भाषा का उपयोग करने वाली किसी चीज़ को कॉल कर रहा है (और जीसीसी के लिए एक विशेषता है जिसका उपयोग आपके स्वयं के प्रिंटफ-जैसे फ़ंक्शंस को सजाने के लिए किया जा सकता है) तो यह सिद्धांत अनुकूलन करने के लिए सिद्धांत रूप से सुरक्षित है। यह अभी भी कार्य करना होगा जैसे कि किसी भी अप्रयुक्त लोगों के साइड इफेक्ट्स होने पर सभी मानकों की गणना की गई थी। – RBerteig

27

Online C Draft Standard (n1256), खंड 7.19.6.1, पैरा 2:।

fprintf समारोह लिखते धारा के उत्पादन धारा के द्वारा की ओर इशारा किया स्ट्रिंग के नियंत्रण में, प्रारूप द्वारा इंगित किया गया है कि आउटपुट के लिए परिवर्तित के बाद के तर्क कैसे हैं।यदि प्रारूप के लिए अपर्याप्त तर्क हैं, तो व्यवहार अपरिभाषित है। यदि तर्क रहते हैं तो प्रारूप समाप्त हो गया है, अतिरिक्त तर्क मूल्यांकन (हमेशा के रूप में) हैं लेकिन अन्यथा अनदेखा कर रहे हैं। fprintf फ़ंक्शन लौटाता है जब स्वरूप स्ट्रिंग का अंत सामने आता है।

अन्य सभी *printf() कार्यों के लिए व्यवहार vprintf() (स्पष्ट रूप से) को छोड़कर एक ही wrt अतिरिक्त तर्क है।

+0

क्या यह उपयोगकर्ता द्वारा लिखित विविधता कार्यों पर लागू होता है? – immibis

+0

@immibis: मुख्य कारण यह नहीं हो सकता है कि कुछ कॉलिंग सम्मेलनों में कैली पॉप पॉप के रूप में स्टैक से निकलता है। इस नियम को अनिवार्य रूप से सामान्य मामले का समर्थन करने के लिए कार्यान्वयन की आवश्यकता है, या 'printf' के लिए बहुत से विशेष उद्देश्य के समर्थन हैं। कैली-पॉप कॉलिंग सम्मेलन वाले अधिकांश सिस्टम इस कारण से भिन्न कार्यों के लिए इसका उपयोग नहीं करते हैं। (और यह भी कि x86 के 'ret imm16' निर्देश के लिए बाइट्स की संख्या को संकलित-समय स्थिर होने के लिए पॉप की आवश्यकता होती है)। 'printf' बहुत अधिक बुरी स्थिति है - कैली की तर्कों की संख्या का पता लगाने की क्षमता: पॉलिमॉर्फिक, कोई सेंटीनेल –

+1

यह स्वीकार्य उत्तर होना चाहिए! यहां सम्मेलन बुलाए जाने की परवाह कौन करता है? मानक को लागू करने के लिए यह सिर्फ एक विवरण है। – Xlea

-2

टिप्पणी: दोनों जीसीसी और बजना उपज चेतावनी:

$ clang main.c 
main.c:4:29: warning: more '%' conversions than data arguments [-Wformat] 
    printf("Gonna %s and %s, %s!", "crash", "burn"); 
          ~^ 
main.c:5:47: warning: data argument not used by format string 
         [-Wformat-extra-args] 
    printf("Gonna %s and %s!", "crash", "burn", "dude"); 
     ~~~~~~~~~~~~~~~~~~     ^
2 warnings generated. 
+0

समस्या तब होती है जब आप अपनी खुद की प्रारूप स्ट्रिंग उत्पन्न करते हैं, जो संकलन समय पर ज्ञात नहीं है। – meneldal

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