जहां तक सी मानक का संबंध है, यदि आप एक फ़ंक्शन पॉइंटर को एक अलग प्रकार के फ़ंक्शन पॉइंटर में डालते हैं और फिर कॉल करते हैं, तो यह अपरिभाषित व्यवहार है । अनुलग्नक J.2 (सूचनात्मक) देखें:
व्यवहार निम्न परिस्थितियों में अपरिभाषित है:
- किसी फ़ंक्शन को कॉल करने के लिए एक पॉइंटर का उपयोग किया जाता है जिसका प्रकार इंगित किए गए प्रकार (6.3.2.3) के साथ संगत नहीं है।
धारा 6.3.2.3, पैराग्राफ 8 पढ़ता है:
एक प्रकार के फ़ंक्शन के लिए एक पॉइंटर को दूसरे प्रकार के फ़ंक्शन के लिए पॉइंटर में परिवर्तित किया जा सकता है और फिर से वापस किया जा सकता है; परिणाम मूल सूचक के बराबर होगा। यदि एक परिवर्तित पॉइंटर का उपयोग किसी फ़ंक्शन को कॉल करने के लिए किया जाता है जिसका प्रकार इंगित किए गए प्रकार के साथ संगत नहीं है, तो व्यवहार अपरिभाषित है।
तो दूसरे शब्दों में, आप एक फ़ंक्शन पॉइंटर को एक अलग फंक्शन पॉइंटर टाइप में डाल सकते हैं, इसे फिर से कास्ट कर सकते हैं, और इसे कॉल कर सकते हैं, और चीजें काम करेंगी।
संगत की परिभाषा कुछ जटिल है। यह खंड 6.7.5.3, पैराग्राफ 15 में पाया जा सकता है:
दो फ़ंक्शन प्रकार संगत होने के लिए, दोनों संगत रिटर्न प्रकार 127 निर्दिष्ट करेंगे ।
इसके अलावा, पैरामीटर प्रकार की सूची, यदि दोनों मौजूद हैं, तो मापदंडों की संख्या और दीर्घवृत्त टर्मिनेटर के उपयोग में सहमत होंगे; संगत मापदंडों में संगत प्रकार होंगे। यदि एक प्रकार में एक पैरामीटर प्रकार की सूची है और दूसरा प्रकार एक फ़ंक्शन घोषणाकर्ता द्वारा निर्दिष्ट किया गया है जो फ़ंक्शन परिभाषा का हिस्सा नहीं है और जिसमें एक खाली पहचानकर्ता सूची है, तो पैरामीटर सूची में एक दीर्घवृत्त टर्मिनेटर और प्रत्येक पैरामीटर का प्रकार नहीं होगा उस प्रकार के साथ संगत रहें जो डिफ़ॉल्ट तर्क पदोन्नति के आवेदन से उत्पन्न होता है। यदि एक प्रकार में एक पैरामीटर प्रकार की सूची होती है और दूसरा प्रकार एक फ़ंक्शन परिभाषा द्वारा निर्दिष्ट होता है जिसमें एक (संभवतः खाली) पहचानकर्ता सूची होती है, तो दोनों पैरामीटर की संख्या में सहमत होंगे और प्रत्येक प्रोटोटाइप पैरामीटर का प्रकार उस प्रकार के साथ संगत होगा जो डिफ़ॉल्ट तर्क प्रोन्नति के आवेदन से संबंधित पहचानकर्ता के प्रकार के परिणामस्वरूप होता है। (प्रकार संगतता और एक समग्र प्रकार के निर्धारण में, फ़ंक्शन या सरणी प्रकार के साथ घोषित प्रत्येक पैरामीटर को समायोजित प्रकार के रूप में लिया जाता है और योग्य प्रकार के साथ घोषित प्रत्येक पैरामीटर को इसके घोषित प्रकार के अयोग्य संस्करण के रूप में लिया जाता है।)
127) यदि दोनों प्रकार के प्रकार 'पुरानी शैली' हैं, तो पैरामीटर प्रकार की तुलना नहीं की जाती है।
यह निर्धारित करने के नियम कि क्या दो प्रकार के संगत हैं, खंड 6.2.7 में वर्णित हैं, और मैं उन्हें यहां उद्धृत नहीं करूंगा क्योंकि वे लंबे हैं, लेकिन आप उन्हें C99 मानक (पीडीएफ) के मसौदे पर पढ़ सकते हैं ।
प्रासंगिक नियम यहाँ खंड 6.7.5.1, पैराग्राफ 2 में है:
संगत होने के लिए दो सूचक प्रकारों के लिए, दोनों को पहचान योग्य रूप से योग्य होना चाहिए और दोनों संगत प्रकारों के लिए संकेत होंगे।
इसलिए, चूंकि यह ए void*
के साथ संगत नहीं है struct my_struct*
, प्रकार void (*)(void*)
का एक फ़ंक्शन पॉइंटर प्रकार के फ़ंक्शन पॉइंटर के साथ संगत नहीं है void (*)(struct my_struct*)
, इसलिए फ़ंक्शन पॉइंटर्स की यह कास्टिंग तकनीकी रूप से अपरिभाषित व्यवहार है।
व्यवहार में, हालांकि, आप कुछ मामलों में कास्टिंग फ़ंक्शन के साथ सुरक्षित रूप से दूर हो सकते हैं। X86 कॉलिंग कन्वेंशन में, स्टैक पर तर्क दिए गए हैं, और सभी पॉइंटर्स समान आकार (x86 में 4 बाइट्स या x86_64 में 8 बाइट्स) हैं। एक फ़ंक्शन पॉइंटर को कॉल करना स्टैक पर तर्कों को आगे बढ़ाने के लिए उबलता है और फ़ंक्शन पॉइंटर लक्ष्य के लिए एक अप्रत्यक्ष कूद करता है, और मशीन कोड स्तर पर स्पष्ट रूप से प्रकार की कोई धारणा नहीं है।
चीजें जो आप निश्चित रूप से नहीं कर सकते कर हैं:
- विभिन्न कॉलिंग सम्मेलनों के फ़ंक्शन पॉइंटर्स के बीच कास्ट करें। आप स्टैक को गड़बड़ कर देंगे और सबसे अच्छा, क्रैश, सबसे कम, एक विशाल गैपिंग सुरक्षा छेद के साथ चुपचाप सफल होंगे। विंडोज प्रोग्रामिंग में, आप अक्सर फ़ंक्शन पॉइंटर्स को पास करते हैं। Win32
stdcall
कॉलिंग कन्वेंशन (जो मैक्रोज़ , और CALLBACK
, का उपयोग करने के लिए सभी कॉलबैक फ़ंक्शन की अपेक्षा करता हैPASCAL
WINAPI
सभी का विस्तार)। यदि आप एक फ़ंक्शन पॉइंटर पास करते हैं जो मानक सी कॉलिंग कन्वेंशन ( cdecl
) का उपयोग करता है , तो परिणाम खराब होगा।
- C ++ में, क्लास मेंबर फंक्शन पॉइंट्स और रेगुलर फंक्शन पॉइंटर्स के बीच कास्ट किया जाता है। यह अक्सर C ++ न्यूबॉब तक यात्रा करता है। कक्षा के सदस्यों के कार्यों में एक छिपा हुआ
this
पैरामीटर होता है, और यदि आप एक सदस्य फ़ंक्शन को नियमित फ़ंक्शन में रखते हैं, तो this
उपयोग करने के लिए कोई वस्तु नहीं है , और फिर से, बहुत खराब परिणाम होगा।
एक और बुरा विचार जो कभी-कभी काम करता है लेकिन अपरिभाषित व्यवहार भी होता है:
- फंक्शन पॉइंटर्स और रेगुलर पॉइंटर्स (जैसे कास्टिंग
void (*)(void)
ए void*
) के बीच कास्टिंग । फंक्शन पॉइंटर्स नियमित रूप से पॉइंटर्स के समान आकार के नहीं होते हैं, क्योंकि कुछ आर्किटेक्चर पर उनके पास अतिरिक्त प्रासंगिक जानकारी हो सकती है। यह शायद x86 पर ठीक काम करेगा, लेकिन याद रखें कि यह अपरिभाषित व्यवहार है।