2010-05-08 16 views
54

क्यों फ़ंक्शन पॉइंटर को संदर्भित करना और कैसे "कुछ भी नहीं" करता है? here से अधिकफ़ंक्शन पॉइंटर का डिफ्रेंसिंग कैसे होता है?

#include<stdio.h> 

void hello() { printf("hello"); } 

int main(void) { 
    (*****hello)(); 
} 

एक टिप्पणी से::

समारोह संकेत भिन्नता केवल ठीक है, लेकिन जिसके परिणामस्वरूप समारोह डेसिग्नेटर तुरंत हो जाएगा

यह है कि मैं क्या बात कर रहा हूँ है फ़ंक्शन पॉइंटर पर वापस परिवर्तित


और एक जवाब here से:

अपसंदर्भन (तरह से आपको लगता है) एक समारोह के सूचक का अर्थ है: एक कोड स्मृति तक पहुँचने के रूप में यह एक डेटा स्मृति होगा।

फ़ंक्शन पॉइंटर को इस तरह से संदर्भित नहीं माना जाता है। इसके बजाए, यह कहा जाता है।

मैं "कॉल" के साथ "dereference" पक्ष नाम का उपयोग करूंगा। यह ठीक है।

वैसे भी: सी इस तरह में बनाया गया है कि दोनों समारोह नाम पहचानकर्ता के रूप में चर पकड़े समारोह के सूचक एक ही मतलब है और साथ ही: कोड स्मृति को पता। और यह पहचानकर्ता या परिवर्तनीय पर कॉल() वाक्यविन्यास या तो का उपयोग कर मेमोरी पर कूदने की अनुमति देता है।


कैसे बिल्कुल करता है एक समारोह सूचक काम के dereferencing?

उत्तर

44

यह बिल्कुल सही सवाल नहीं है। सी के लिए, कम से कम, सही प्रश्न

रावल्यू संदर्भ में फ़ंक्शन मान का क्या होता है?

(एक rvalue संदर्भ कहीं भी जहां यह एक मूल्य के रूप में, बल्कि एक स्थान — से मूल रूप से कहीं भी एक काम के बाएं हाथ की ओर पर छोड़कर इस्तेमाल किया जाना चाहिए एक नाम या अन्य संदर्भ प्रतीत होता है। नाम ही से आता है दाएं - असाइनमेंट के किनारे पक्ष।)

ठीक है, तो एक रावल संदर्भ में फ़ंक्शन मान का क्या होता है? यह तत्काल और निहित रूप से मूल कार्य मूल्य में एक सूचक में परिवर्तित हो जाता है। यदि आप * के साथ पॉइंटर को अस्वीकार करते हैं, तो आपको एक ही फ़ंक्शन वैल्यू दोबारा मिल जाएगा, जो तत्काल और निहित रूप से एक सूचक में परिवर्तित हो जाता है। और आप इसे जितनी बार चाहें उतनी बार कर सकते हैं।

दो समान प्रयोगों तुम कोशिश कर सकते हैं:

  • क्या होगा यदि आप एक lvalue संदर्भ — एक काम के बाएं हाथ की ओर में एक समारोह संकेतक भिन्नता होता है। (उत्तर, आप क्या उम्मीद के बारे में हो सकता है अगर आप ध्यान रखें कि कार्यों अपरिवर्तनीय हैं जाएगा।)

  • एक सरणी मूल्य भी एक lvalue संदर्भ में एक सूचक में बदल जाती है, लेकिन यह के लिए सूचक में बदल जाती है तत्व प्रकार, सरणी के लिए सूचक के लिए नहीं। इसलिए इसे अस्वीकार करने से आप एक तत्व नहीं देंगे, एक सरणी नहीं, और आपके द्वारा दिखाए गए पागलपन नहीं होते हैं।

उम्मीद है कि इससे मदद मिलती है।

पीएस क्यों एक फ़ंक्शन मान को निश्चित रूप से एक सूचक में परिवर्तित किया जाता है, इसका उत्तर यह है कि फ़ंक्शन पॉइंटर्स का उपयोग करने वाले उपयोग के लिए, यह एक शानदार सुविधा है कि & का हर जगह उपयोग न करें। एक दोहरी सुविधा भी है: कॉल स्थिति में फ़ंक्शन पॉइंटर स्वचालित रूप से फ़ंक्शन मान में परिवर्तित हो जाता है, इसलिए आपको फ़ंक्शन पॉइंटर के माध्यम से कॉल करने के लिए * लिखना नहीं है।

पी.पी.एस. सी कार्यों के विपरीत, सी ++ फ़ंक्शन ओवरलोड हो सकते हैं, और मैं इस बात पर टिप्पणी करने के लिए योग्य नहीं हूं कि सी ++ में अर्थशास्त्र कैसे काम करता है।

+0

क्या आप कृपया "..." फ़ंक्शन को मूल कार्य मूल्य में रूपांतरित रूप से परिवर्तित कर सकते हैं? क्या आप फ़ंक्शन के रिटर्न वैल्यू का जिक्र कर रहे हैं? यदि हां, तो इसका मतलब यह है कि कंपाइलर स्वचालित रूप से उस रिटर्न वैल्यू को एक लाइवल्यू के रूप में रखता है, हालांकि फ़ंक्शन रिटर्न वैल्यू एक रावल्यू था। धन्यवाद! – shorttermmem

5

सी ++ 03 §4.3/1:

समारोह प्रकार टी का एक lvalue "टी करने के लिए सूचक" प्रकार का एक rvalue में बदला जा सकता परिणाम कार्य करने के लिए एक सूचक है।

आप इस तरह के एकल * ऑपरेटर के रूप में एक समारोह के संदर्भ, पर गलत आपरेशन प्रयास करते हैं, पहली बात यह है भाषा की कोशिश करता है एक मानक रूपांतरण है। यह float पर जोड़ने के दौरान int को परिवर्तित करने जैसा है। फ़ंक्शन संदर्भ पर * का उपयोग करने से भाषा आपके पॉइंटर को इसके बजाय ले जाती है, जो आपके उदाहरण में, वर्ग 1 है।

एक अन्य मामला जहां यह लागू होता है जब फ़ंक्शन पॉइंटर असाइन करते हैं।

void f() { 
    void (*recurse)() = f; // "f" is a reference; implicitly convert to ptr. 
    recurse(); // call operator is defined for pointers 
} 

ध्यान दें कि यह काम दूसरा रास्ता नहीं है।

void f() { 
    void (&recurse)() = &f; // "&f" is a pointer; ERROR can't convert to ref. 
    recurse(); // OK - call operator is *separately* defined for references 
} 

समारोह संदर्भ चर अच्छा है क्योंकि वे संकेत संकलक कि एक अप्रत्यक्ष शाखा, अनावश्यक हो सकता है अगर एक enclosing दायरे में प्रारंभ करने के लिए (सिद्धांत रूप में, मैं परीक्षण किया कभी नहीं किया है) कर रहे हैं।

सी 99 में, फ़ंक्शन पॉइंटर को संदर्भित करने से फ़ंक्शन डिज़ाइनर उत्पन्न होता है। §6.3.2.1/4:

एक फ़ंक्शन डिज़ाइनर एक अभिव्यक्ति है जिसमें फ़ंक्शन प्रकार है। सिवाय इसके कि जब यह आकार के ऑपरेटर या यूनरी & ऑपरेटर का ऑपरेंड होता है, तो 'फ़ंक्शन रिटर्निंग टाइप' प्रकार वाले फ़ंक्शन डिज़ाइनर को एक अभिव्यक्ति में परिवर्तित किया जाता है जिसमें '' फ़ंक्शन रिटर्निंग प्रकार 'के लिए पॉइंटर होता है।

यह नॉर्मन के उत्तर की तरह है, लेकिन विशेष रूप से सी 99 में राजस्व की कोई अवधारणा नहीं है।

+0

"_on a function context_" असल में, ** एक अभिव्यक्ति में संदर्भ प्रकार ** नहीं हो सकता है। एक अभिव्यक्ति rvalue या lvalue हो सकता है। – curiousguy

2

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

प्रोग्रामर फ़ंक्शन पॉइंटर को अस्वीकार करते समय आप क्या करते हैं? क्या आप मशीन कोड के पहले (या 8) बाइट्स लेते हैं और एक पॉइंटर के रूप में दोबारा परिभाषित करते हैं? बाधाएं लगभग 2 अरब हैं जो यह काम नहीं करेगी। क्या आप यूबी घोषित करते हैं? उसमें से बहुत पहले से ही जा रहा है। या आप सिर्फ प्रयास को अनदेखा करते हैं? आप जवाब जानते हैं।

+4

अगर मैं संकलक लेखक थे तो मैं इसे अवैध बना दूंगा। यह कुछ हद तक भ्रामक जवाब है। – rpjohnst

1

फ़ंक्शन पॉइंटर का डेरफ़्रेंसिंग वास्तव में कैसे काम करता है?

दो चरण। पहला कदम संकलन समय पर दूसरा है, रनटाइम पर दूसरा।

कदम एक में, संकलक देखता है यह एक संकेतक और एक संदर्भ में जो कि सूचक dereferenced है (जैसे (*pFoo)()) नहीं हुए हैं तो यह उस स्थिति, कोड चरण में इस्तेमाल किया जाएगा के लिए कोड उत्पन्न करता है 2.

चरण 2 में, रनटाइम पर कोड निष्पादित किया जाता है। पॉइंटर में कुछ बाइट होते हैं जो दर्शाते हैं कि कौन सा फ़ंक्शन अगले निष्पादित किया जाना चाहिए। इन बाइट्स को किसी भी तरह से सीपीयू में लोड किया जाता है। एक सामान्य मामला एक सीपीयू है जो एक स्पष्ट CALL [register] निर्देश के साथ है। ऐसे सिस्टम पर, फ़ंक्शन पॉइंटर बस स्मृति में किसी फ़ंक्शन का पता हो सकता है, और अपर्याप्त कोड उस पते को CALL [register] निर्देश के बाद उस पते को लोड करने से कुछ भी नहीं करता है।

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