2015-05-28 2 views
7

मुझे पता है कि फ़ंक्शन प्रोटोटाइप को C++ में अनिवार्य है यदि फ़ंक्शन को main() फ़ंक्शन के बाद परिभाषित किया गया है, लेकिन यह सी में वैकल्पिक (लेकिन अनुशंसित) है। मैंने हाल ही में एक सरल प्रोग्राम लिखा है जो 2 संख्याओं को जोड़ता है, लेकिन गलती से तर्क पारित करते समय एक अल्पविराम के बजाय डॉट ऑपरेटर।सी में फ़ंक्शन कॉल करते समय इसे पैरामीटर की अपर्याप्त संख्या क्यों पारित करने की अनुमति है?

#include <stdio.h> 
int main() 
{ 
    printf("sum is: %d",add(15.30)); // oops, uses dot instead of comma 
} 

int add(int a,int b) 
{ 
    return a+b; 
} 

उपरोक्त कार्यक्रम में अगर add(int,int) समारोह main() समारोह से पहले परिभाषित किया गया है तो निश्चित रूप से कार्यक्रम को संकलित करने के असफल हो जायेगी। ऐसा इसलिए है क्योंकि फ़ंक्शन को आवश्यकतानुसार कॉल करते समय कम पैरामीटर पारित किए जाते हैं।

लेकिन, उपरोक्त प्रोग्राम संकलित और ठीक क्यों चल रहा है - आउटपुट के रूप में कुछ बड़े कचरा मूल्य प्रदान करते हैं? क्या कारण है? क्या फ़ंक्शन प्रोटोटाइप का उपयोग करना बेहतर है ताकि संकलक प्रकार के मिलान और फ़ंक्शन आमंत्रण से जुड़े किसी भी अन्य त्रुटियों का पता लगा सके?

क्या यह अपरिभाषित व्यवहार है?

+3

यह अपरिभाषित व्यवहार है, इस तरह की गलती को रोकने के लिए चेतावनियों का उपयोग करें। –

+0

आईआईआरसी, वैरगास सिंटैक्स की शुरूआत से पहले, लोगों को पैरामीटर का एक गुच्छा घोषित करके नकली varargs होगा और फिर उनमें से कुछ को पारित नहीं किया जाएगा। यह कभी परिभाषित व्यवहार नहीं था, लेकिन लोगों ने इसे किया, और बाद में संकलक संस्करणों को पहले कोड का समर्थन करना पड़ा। – user2357112

+0

@ user2357112: क्या आप वाकई उन दिनों में परिभाषित व्यवहार नहीं थे जब समापन कोष्ठक और उद्घाटन ब्रेस के बीच पैरामीटर सूचीबद्ध किए गए थे? मैंने सोचा कि मैंने (1 9 80 के दशक) यूनिक्स को कुछ फ़ंक्शंस का उपयोग किया था, जो कि पहले पैरामीटर का उपयोग करने के लिए तय किया गया था कि बाद के पैरामीटर की जांच करना है, जिनके बाद के पैरामीटर कभी-कभी अप्रयुक्त किए जाते थे। – supercat

उत्तर

9

यदि सी आपको एक ऐसे फ़ंक्शन को कॉल करता है जिसे अभी तक घोषित नहीं किया गया है, तो यह एक निहित घोषणा उत्पन्न करेगा, किसी भी तर्क को लेकर, int की वापसी मान लेगा। आपके उदाहरण में यह int add(); की निहित घोषणा बना रहा है यह निश्चित रूप से गलत है।

रनटाइम पर, ऐड फ़ंक्शन को अपने स्टैक पर एक डबल डबल प्राप्त होगा, और बाइट्स को भ्रमित और अपरिभाषित तरीके से समझने का प्रयास करें।

सी 99 ने इस नियम को बदल दिया और अव्यवस्थित कार्यों को कॉल करने से मना कर दिया, लेकिन अधिकांश कंपाइलर अभी भी आपको चेतावनी के बावजूद ऐसा करने देते हैं।

printf अप्रासंगिक है।

+0

इस उत्तर पर विस्तार करने के लिए: यदि आप या तो अपने फ़ंक्शन के लिए प्रोटोटाइप घोषित करना चाहते थे, या फ़ाइल में इसे कॉल करने से पहले इसे परिभाषित करना चाहते हैं, तो आपको एक उचित त्रुटि संदेश मिलेगा। – larsks

+0

@larsks: यह पहले से ही मेरे प्रश्न में उल्लिखित है .... – Destructor

+0

"ऐड फ़ंक्शन को इसके ढेर पर एक डबल डबल प्राप्त होगा, और बाइट्स को भ्रमित और अपरिभाषित तरीके से समझने का प्रयास करें।" हम्म, लेकिन क्या होगा यदि 'int add (int a, int b) {if (a == 15) 0 लौटाएं; एक + बी;} वापस लौटें, क्या यह ठीक रहेगा? – chux

0

सी में फ़ंक्शन कॉल करते समय इसे अपर्याप्त संख्याओं को पारित करने की अनुमति क्यों है?

यह नहीं है।

तथ्य यह है कि आपका कंपाइलर चेतावनी/त्रुटि नहीं देता है इसका मतलब यह नहीं है कि यह सही है।

+2

यह सी 0 9 0 में पूरी तरह से मान्य है और कारण और निहित घोषणा (दुर्भाग्यवश बाद की परिभाषा के साथ असंगत है) हालांकि एक अच्छा संकलक चेतावनी देना चाहिए। – johannes

+0

@ जोहेंस: कुछ कंपाइलर 'int add() 'या' int add (int, int)' के रूप में घोषित विधियों के लिए दो 'int' मानों को पार करते समय समान कोड उत्पन्न करेंगे; ऐसे कंपाइलर बाद की परिभाषा को पहले के साथ संगत मान सकते हैं। – supercat

+0

@ जोहान्स ** आप गलत हैं। ** पैरामीटर की गलत संख्या वाले फ़ंक्शन को कॉल करना ** अपरिभाषित व्यवहार ** यहां तक ​​कि C90 में भी है। ** प्रश्न पढ़ें। ** यह पैरामीटर सूची निर्दिष्ट नहीं है (जो दुर्भाग्यवश C90 में मान्य था), लेकिन फ़ंक्शन को पर्याप्त तर्क के साथ कॉल करने के बारे में नहीं है। बस उद्धरण देखें: "** ** ** पैरामीटर की अपर्याप्त संख्या पास करें" - यह कहता है "पास" और "घोषित नहीं"। –

10

सी, जब तरीकों की तरह दिखाई देता के प्रारंभिक दिनों में:

int add(a,b) 
    int a; 
    int b; 
{ 
    return a+b; 
} 

int x; दिया एक बयान add(3,5); की तरह कोड उत्पन्न होगा:

push 5 
push 3 
call _add 
store r0,_x 

संकलक में कुछ भी पता की जरूरत नहीं होगी उपरोक्त कोड उत्पन्न करने के लिए add विधि प्रकार से परे विधि (जिसे यह int के रूप में अनुमान लगाया जा सकता है); यदि विधि मौजूद हो गई है, तो यह लिंक समय पर खोजा जाएगा। कोड जो नियमित रूप से अपेक्षित से अधिक पैरामीटर प्रदान करता है, उन मानों को स्टैक पर धक्का देगी जहां उन्हें कुछ समय बाद कोड पॉप किए जाने तक अनदेखा कर दिया जाएगा।

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

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

एएनएसआई सी ने फ़ंक्शन प्रोटोटाइप के उपयोग को मानकीकृत किया; यदि संकलक int add(int a, int b) जैसे int add(int a, int b) पर एक परिभाषा या घोषणा देखता है, तो यह के दो तर्कों को पास करता है, भले ही कॉलर कुछ और प्रदान करता हो (उदाहरण के लिए यदि कॉलर double पास करता है, तो इसका मूल्य होगा कॉल से पहले int टाइप करने के लिए मजबूर होना चाहिए)। संकलक ने को एक त्रुटि के परिणामस्वरूप को बहुत कम पैरामीटर पास करने का प्रयास किया है।

उपरोक्त कोड में, add पर कॉल करने से पहले संकलक के पास कोई संकेत है कि विधि क्या अपेक्षा कर रही है। जबकि नई बोलीयां इस तरह के उपयोग की अनुमति नहीं देगी, पुराने लोगों को संकलक डिफ़ॉल्ट रूप से मानते हैं कि add एक ऐसा तरीका है जो कॉलर की आपूर्ति के किसी भी तर्क को ले जाएगा और int लौटाएगा। उपर्युक्त मामले में, कॉल किया गया कोड संभवतः दो शब्दों को स्टैक पर धकेलने की उम्मीद करेगा, लेकिन double निरंतर शायद दो या चार शब्दों के रूप में धक्का दिया जाएगा। इस प्रकार, विधि a और bdouble मान 15.3 के बाइनरी प्रतिनिधित्व से दो शब्द प्राप्त करने के लिए शायद प्राप्त होगी।

बीटीडब्ल्यू, यहां तक ​​कि संकलक जो संकेतित वाक्यविन्यास स्वीकार करते हैं, लगभग हमेशा स्क्वॉक करेंगे यदि किसी फ़ंक्शन जिसका प्रकार निहित रूप से माना जाता है उसे int के अलावा कुछ और लौटने के रूप में घोषित किया गया है; यदि पुराने फ़ंक्शन सिंटैक्स के अलावा किसी अन्य फ़ंक्शन का उपयोग करके परिभाषित किया गया है तो कई भी squawk करेंगे। जबकि पहले सी कंपाइलर्स ने हमेशा ढेर पर फ़ंक्शन तर्कों को धक्का दिया, नए लोग आमतौर पर उन्हें रजिस्टरों में उम्मीद करते हैं। इस प्रकार, यदि एक घोषित करने के लिए

/* Old-style declaration for function that accepts whatever it's given 
    and returns double */ 

double add(); 

तो कोड add(12.3,4.56) स्टैक पर दो double मूल्यों धक्का और add समारोह कहेंगे थे, लेकिन इसके बजाय घोषित करता है, तो एक था:

double add(double a, double b); 
तो कई सिस्टम पर

कोड add(12.3,4.56)12.3 के साथ फ़्लोटिंग-पॉइंट रजिस्टर 0 लोड करेगा और कॉल से पहले 4.56 के साथ फ़्लोटिंग-पॉइंट रजिस्टर 1 (कुछ भी नहीं दबाएगा)। नतीजतन, घोषणा की दो शैलियों संगत नहीं हैं। ऐसी प्रणालियों पर, इसे घोषित किए बिना किसी विधि को कॉल करने का प्रयास करते हुए, और बाद में इसे नए-शैली सिंटैक्स का उपयोग करके उसी संकलन इकाई के भीतर घोषित करने से संकलन त्रुटि उत्पन्न होगी। इसके अलावा, कुछ ऐसे सिस्टम उन कार्यों के नामों का उपसर्ग करते हैं जो दो अंडरस्कोर के साथ पंजीकरण तर्क लेते हैं और जो अंडरस्कोर के साथ नहीं होते हैं, इसलिए यदि विधि को नए-शैली सिंटैक्स का उपयोग करके परिभाषित किया गया था लेकिन प्रोटोटाइप को देखते हुए कंपाइलर के बिना बुलाया गया था, तो कंपाइलर _add पर कॉल करने का प्रयास करेंगे, भले ही फ़ंक्शन को __add कहा गया हो। इससे लिंकर एक निष्पादन योग्य उत्पन्न करने के बजाय प्रोग्राम को अस्वीकार कर देगा जो काम नहीं करेगा।

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

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