2012-01-23 19 views
14

संभव डुप्लिकेट:
What is the point of function pointers?फ़ंक्शन पॉइंटर्स का उपयोग क्या है?

मैं समझने के लिए जहां व्यावहारिक स्थितियों में समारोह संकेत उपयोग किया जाता है कोशिश कर रहा हूँ। और कोई भी मुझे एक व्यावहारिक उदाहरण दे सकता है जहां हमें किसी अन्य फ़ंक्शन के लिए तर्क के रूप में कार्य करना होगा।

+0

यह सी # है, लेकिन यह सी/सी ++ में भी जाता है और फ़ंक्शन पॉइंटर्स की व्यावहारिकता: http://stackoverflow.com/questions/667410/the-benefits-of-using-function-pointers – Algorhythm

उत्तर

18

फ़ंक्शन पॉइंटर्स उपयोगी हो सकते हैं जब आप callback mechanism बनाना चाहते हैं, और किसी फ़ंक्शन के पते को किसी अन्य फ़ंक्शन में पास करने की आवश्यकता है।

उदाहरण के लिए गतिशील रूप से कॉल करने के लिए, जब आप कार्यों की एक सरणी संग्रहित करना चाहते हैं तो वे भी उपयोगी हो सकते हैं।

8

एक आम उपयोग callback function को लागू करना है।

qsort लाइब्रेरी फ़ंक्शन का उपयोग करके कुछ सॉर्ट करने का प्रयास करें। यह अंतिम पैरामीटर आपके द्वारा लिखे गए तुलनित्र फ़ंक्शन के लिए एक सूचक है।

5

पहली बात जो मेरे दिमाग में एक बहुत ही उपयोगी अनुप्रयोग के रूप में आती है वह एक बटन है। निम्नलिखित कोड डालें:

int buttonID = CreateButton ("Click Me!", 100, 100, 200, 100, onClick); 

यह चौड़ाई 200 और ऊंचाई 100 हर बार जब आप इसे क्लिक के साथ (100,100) में एक बटन बनाना होगा, onClick कहा जाता है।

मैं व्यक्तिगत विंडोज एपीआई रैपर में कुछ समान उपयोग करता हूं। यह बटन बनाने आदि इतना आसान बनाता है।

3

खैर, # 1 स्टॉक उत्तर है: qsort। तुलनित्र दिनचर्या qsort का उपयोग एक फ़ंक्शन पॉइंटर के रूप में पारित किया जाएगा। कई अन्य "जेनेरिक एल्गोरिदम" फ़ंक्शंस तुलनात्मक रूप से इसी तरह से लेते हैं; जैसे शायद एक हैशटेबल कार्यान्वयन आपके हैश फ़ंक्शन को स्वीकार कर सकता है।

सी-भाषा जीयूआई टूलकिट्स और एप्लिकेशन फ्रेमवर्क (उदा। जीनोम/जीटीके +/ग्लिब) अक्सर टाइमर या उपयोगकर्ता इंटरफ़ेस ईवेंट के लिए फ़ंक्शन पॉइंटर्स को "कॉलबैक" के रूप में स्वीकार करते हैं। (ईजी: "जब भी यह बटन क्लिक किया जाता है" इस क्रिया को कॉल करें "या" ... जब भी यह टाइमर समाप्त हो जाता है ")

वास्तव में, सी में अधिकांश" ओओपी-जैसे "या" ईवेंट-संचालित "कोड एक के लिए फ़ंक्शन पॉइंटर्स स्वीकार करेगा इसी तरह का कारण

4

समारोह संकेत के लिए दो प्रमुख का उपयोग करता है के होते हैं:

  • कॉलबैक - ईवेंट हैंडलर्स, पार्सर विशेषज्ञता, तुलनित्र समारोह पारित करने के लिए इस्तेमाल किया ...
  • प्लग इन और एक्सटेंशन - कार्यों के लिए संकेत दिए गए प्लग इन या द्वारा प्रदान की लाइब्रेरी एक्सटेंशन को मानक फ़ंक्शनियो GetProcAddress, dlsym या इसी तरह से इकट्ठा किया जाता है, जो फ़ंक्शन पहचानकर्ता को नाम के रूप में लेता है और फ़ंक्शन पॉइंटर लौटाता है। ओपनजीएल जैसे एपीआई के लिए बिल्कुल महत्वपूर्ण है।
1

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

// All odd numbers are before even numbers 
int cmpoddeven(const void *xp, const void *yp) { 
    int x = *((int*) xp); 
    int y = *((int*) yp); 
    if(x == y) 
    return 0; 
    if(x % 2 == y % 2) { 
    return (x < y ? -1 : 1); 
    if(x % 2 == 1) 
    return -1; 
    return 1; 
} 

int main() { 
    int array[] = {1, 2, 3, 4, 5}; 
    // calling qsort with cmpoddeven as the comparison function 
    qsort(array, 5, sizeof(int), &cmpoddeven); 
    // array == {1, 3, 5, 2, 4}; 
} 
1

ज्यादातर मामलों में, यह अनिवार्य रूप से dependency inversion करने का सी तरीका है। विकी लेख कहता है:

ए उच्च स्तरीय मॉड्यूल निम्न-स्तर मॉड्यूल पर निर्भर नहीं होना चाहिए। दोनों को अवशोषण पर निर्भर होना चाहिए। बी। सार तत्वों को विवरणों पर निर्भर नहीं होना चाहिए। विवरण abstractions पर निर्भर होना चाहिए।

qsort की क्लासिक उदाहरण अर्थ है कि उच्च स्तर की तरह समारोह डेटा के प्रकार, आकार, या तुलना विधि पर निर्भर नहीं करता में यह करता क्रमबद्ध करना। तो यदि आप qsort() इनट्स की एक सरणी है, तो विवरण sizeof(int) और आपकी तुलना कार्यान्वयन हैं। अमूर्तता मनमाने ढंग से आकार के तत्वों और एक कार्य है जो उस प्रकार के तत्वों की तुलना करता है।

यह भी देखें: Inversion of Control

मुझे आश्चर्य है कि किसी ने उदाहरण के रूप में pthread_create() का उल्लेख नहीं किया है।

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

4

कॉलबैक दिनचर्या अब तक का सबसे आम परिदृश्य प्रतीत होता है। हालांकि, कई अन्य हैं ...

परिमित राज्य मशीनें जहां (बहु-आयामी) सरणी के तत्व दिनचर्या को इंगित करते हैं जो अगले राज्य को संसाधित/संभालता है। यह एक स्थान (सरणी) में एफएसएम की परिभाषा को रखता है।

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

कोड अव्यवस्था को कम करने से मैंने पिछले उदाहरण में इसे स्पर्श किया। जैसे ...

switch (a) { 
case 0: 
    func0(); 
    break; 
case 1: 
    func1(); 
    break; 
case 2: 
    func2(); 
    break; 
case 3: 
    func3(); 
    break; 
default: 
    funcX(); 
    break; 
} 

उदाहरण के लिए सरल किया जा सकता है ...

/* This declaration may be off a little, but I am after the essence of the idea */ 
void (*funcArray)(void)[] = {func0, func1, func2, func3, funcX}; 
... appropriate bounds checking on 'a' ... 
funcArray[a](); 

वहाँ कई और अधिक कर रहे हैं। उम्मीद है की यह मदद करेगा।

+1

FWIW, घोषणा 'शून्य होना चाहिए (* funcArray []) (शून्य) = {func0, func1, func2 ...}; '(' funcArray' कार्यों के लिए पॉइंटर्स की एक सरणी है ...)। घोषणा याद रखें नकल नकल; यदि कोड में अभिव्यक्ति 'funcArray [a]() 'है, तो घोषणा उसी तरह संरचित की जा रही है। –

+1

@ जॉनबोड - बहुत सराहना की। हालांकि मैं 20 से अधिक वर्षों से सी के साथ काम कर रहा हूं, फिर भी मैं फ़ंक्शन पॉइंटर सिंटैक्स पर मिश्रित हूं। आम तौर पर मैं पहले फंक्शन पॉइंटर टाइप करने के लिए आसान (मेरे लिए) मार्ग का चयन करता हूं और फिर typedef'ed चर की सरणी घोषित करता हूं। – Sparky

+0

@ स्पार्की टाइपिफ़ के संबंध में सहमत हैं। यह फ़ंक्शन पॉइंटर्स या पॉइंटर्स के एरेज़ जैसे फ़ंक्शन पॉइंटर्स को पढ़ने के लिए बहुत आसान बनाता है। –

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