सी में फ़ंक्शन पॉइंटर्स के साथ मुझे कुछ अनुभव हुआ।
तो अपने स्वयं के सवालों के जवाब देने की परंपरा के साथ, मैंने बहुत मूल बातें का एक छोटा सा सारांश बनाने का फैसला किया, जिनके लिए विषय को त्वरित गोता लगाने की आवश्यकता है।
सी में फ़ंक्शन पॉइंटर्स के साथ मुझे कुछ अनुभव हुआ।
तो अपने स्वयं के सवालों के जवाब देने की परंपरा के साथ, मैंने बहुत मूल बातें का एक छोटा सा सारांश बनाने का फैसला किया, जिनके लिए विषय को त्वरित गोता लगाने की आवश्यकता है।
जवाबों:
आइए एक मूल कार्य से शुरू करें, जिसकी ओर हम इशारा करेंगे :
int addInt(int n, int m) {
return n+m;
}
पहली बात, चलो एक सूचक को एक फ़ंक्शन को परिभाषित करते हैं जो 2 int
एस प्राप्त करता है और एक रिटर्न देता है int
:
int (*functionPtr)(int,int);
अब हम सुरक्षित रूप से अपने कार्य को इंगित कर सकते हैं:
functionPtr = &addInt;
अब हमारे पास फ़ंक्शन के लिए एक पॉइंटर है, आइए इसका उपयोग करें:
int sum = (*functionPtr)(2, 3); // sum == 5
किसी अन्य फ़ंक्शन के लिए पॉइंटर पास करना मूल रूप से समान है:
int add2to3(int (*functionPtr)(int, int)) {
return (*functionPtr)(2, 3);
}
हम रिटर्न पॉइंट्स में फंक्शन पॉइंटर्स का उपयोग कर सकते हैं (ऊपर रखने की कोशिश करें, यह गड़बड़ हो जाता है):
// this is a function called functionFactory which receives parameter n
// and returns a pointer to another function which receives two ints
// and it returns another int
int (*functionFactory(int n))(int, int) {
printf("Got parameter %d", n);
int (*functionPtr)(int,int) = &addInt;
return functionPtr;
}
लेकिन इसका उपयोग करने के लिए बहुत अच्छा है typedef
:
typedef int (*myFuncDef)(int, int);
// note that the typedef name is indeed myFuncDef
myFuncDef functionFactory(int n) {
printf("Got parameter %d", n);
myFuncDef functionPtr = &addInt;
return functionPtr;
}
pshufb
, यह धीमा है, इसलिए पहले का कार्यान्वयन अभी भी तेज है। x264 / x265 बड़े पैमाने पर इसका उपयोग करते हैं, और खुले स्रोत हैं।
C में फ़ंक्शन पॉइंटर्स का उपयोग C में ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग करने के लिए किया जा सकता है।
उदाहरण के लिए, निम्नलिखित पंक्तियाँ C में लिखी गई हैं:
String s1 = newString();
s1->set(s1, "hello");
हां, ->
और एक new
ऑपरेटर की कमी एक मृत दे रही है, लेकिन यह निश्चित रूप से लगता है कि हम कुछ String
वर्ग के पाठ को सेट कर रहे हैं "hello"
।
फ़ंक्शन पॉइंटर्स का उपयोग करके, सी में तरीकों का अनुकरण करना संभव है ।
यह कैसे पूरा किया जाता है?
String
वर्ग वास्तव में एक है struct
समारोह संकेत दिए गए जो अनुकरण तरीकों के लिए एक रास्ता के रूप में कार्य के एक समूह के साथ। निम्नलिखित String
वर्ग की आंशिक घोषणा है :
typedef struct String_Struct* String;
struct String_Struct
{
char* (*get)(const void* self);
void (*set)(const void* self, char* value);
int (*length)(const void* self);
};
char* getString(const void* self);
void setString(const void* self, char* value);
int lengthString(const void* self);
String newString();
जैसा कि देखा जा सकता है, String
वर्ग के तरीके वास्तव में घोषित फ़ंक्शन के फ़ंक्शन हैं। के उदाहरण को तैयार करने के लिए String
, newString
फ़ंक्शन को उनके संबंधित कार्यों के लिए फ़ंक्शन पॉइंटर्स सेट करने के लिए कहा जाता है:
String newString()
{
String self = (String)malloc(sizeof(struct String_Struct));
self->get = &getString;
self->set = &setString;
self->length = &lengthString;
self->set(self, "");
return self;
}
उदाहरण के लिए, विधि को getString
लागू करने के द्वारा कहा जाने वाला फ़ंक्शन get
निम्नलिखित के रूप में परिभाषित किया गया है:
char* getString(const void* self_obj)
{
return ((String)self_obj)->internal->value;
}
एक बात जिस पर ध्यान दिया जा सकता है वह यह है कि किसी वस्तु के उदाहरण की कोई अवधारणा नहीं है और ऐसी विधियाँ हैं जो वास्तव में एक वस्तु का एक हिस्सा हैं, इसलिए प्रत्येक आह्वान पर एक "आत्म वस्तु" को पारित किया जाना चाहिए। (और internal
यह सिर्फ एक छिपी हुई बात है struct
जिसे पहले कोड लिस्टिंग से हटा दिया गया था - यह सूचना छिपाने का एक तरीका है, लेकिन यह फ़ंक्शन पॉइंटर्स के लिए प्रासंगिक नहीं है।)
इसलिए, ऐसा करने में सक्षम होने के बजाय s1->set("hello");
, व्यक्ति को कार्रवाई करने के लिए ऑब्जेक्ट में पास होना चाहिए s1->set(s1, "hello")
।
उस मामूली व्याख्या के साथ अपने आप को बाहर के संदर्भ में पारित करने के लिए, हम अगले भाग में जाएंगे, जो सी में विरासत है ।
मान लीजिए कि हम एक उपवर्ग बनाना चाहते हैं String
, एक कहते हैं ImmutableString
। स्ट्रिंग को अपरिवर्तनीय बनाने के लिए, यह set
विधि सुलभ नहीं होगी, जबकि एक्सेस करने के लिए get
और length
"कंस्ट्रक्टर" को बनाए रखने के लिए मजबूर करती है char*
:
typedef struct ImmutableString_Struct* ImmutableString;
struct ImmutableString_Struct
{
String base;
char* (*get)(const void* self);
int (*length)(const void* self);
};
ImmutableString newImmutableString(const char* value);
मूल रूप से, सभी उपवर्गों के लिए, उपलब्ध विधियाँ एक बार फिर से कार्य बिंदु हैं। इस बार, set
विधि के लिए घोषणा मौजूद नहीं है, इसलिए, इसे ए में नहीं बुलाया जा सकता है ImmutableString
।
के कार्यान्वयन के लिए ImmutableString
, एकमात्र प्रासंगिक कोड "निर्माता" फ़ंक्शन है newImmutableString
:
ImmutableString newImmutableString(const char* value)
{
ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));
self->base = newString();
self->get = self->base->get;
self->length = self->base->length;
self->base->set(self->base, (char*)value);
return self;
}
त्वरित रूप से ImmutableString
कार्य करने में , फ़ंक्शन पॉइंटर्स get
और length
विधियों को वास्तव में चर के माध्यम से जाकर String.get
और String.length
विधि को संदर्भित करता है , base
जो कि एक आंतरिक संग्रहित String
वस्तु है।
एक फ़ंक्शन पॉइंटर का उपयोग एक सुपरक्लास से एक विधि का उत्तराधिकार प्राप्त कर सकता है।
हम आगे C में बहुरूपता जारी रख सकते हैं ।
यदि उदाहरण के लिए हम किसी कारण से कक्षा में हर समय length
लौटने के लिए विधि के व्यवहार को बदलना चाहते थे , तो यह करना होगा:0
ImmutableString
length
विधि के रूप में काम करने वाला है।length
विधि पर सेट करें ।ओवरराइडिंग length
विधि ImmutableString
जोड़ने से प्रदर्शन किया जा सकता है lengthOverrideMethod
:
int lengthOverrideMethod(const void* self)
{
return 0;
}
फिर, length
कंस्ट्रक्टर में विधि के लिए फंक्शन पॉइंटर को नीचे झुका दिया जाता है lengthOverrideMethod
:
ImmutableString newImmutableString(const char* value)
{
ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));
self->base = newString();
self->get = self->base->get;
self->length = &lengthOverrideMethod;
self->base->set(self->base, (char*)value);
return self;
}
अब कक्षा length
में विधि के लिए समान व्यवहार रखने के बजाय , अब विधि फ़ंक्शन में परिभाषित व्यवहार को संदर्भित करेगी ।ImmutableString
String
length
lengthOverrideMethod
मुझे एक डिस्क्लेमर जोड़ना होगा कि मैं अभी भी सीख रहा हूं कि सी में ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग स्टाइल के साथ कैसे लिखना है, इसलिए संभवतः ऐसे बिंदु हैं जो मैंने अच्छी तरह से नहीं समझाए, या ओओपी को लागू करने के लिए सबसे अच्छा होने के संदर्भ में केवल चिह्न हो सकता है। सी। लेकिन मेरा उद्देश्य फ़ंक्शन पॉइंटर्स के कई उपयोगों में से एक का वर्णन करने का प्रयास करना था।
C में ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग कैसे करें, इस बारे में अधिक जानकारी के लिए, कृपया निम्नलिखित प्रश्नों का संदर्भ लें:
ClassName_methodName
फ़ंक्शन नामकरण सम्मेलन से चिपकाकर । तभी आपको C ++ और पास्कल में समान रनटाइम और स्टोरेज लागत मिलती है।
निकाल दिया जाने वाला मार्गदर्शिका: हाथ से अपना कोड संकलित करके x86 मशीनों पर GCC में फ़ंक्शन पॉइंटर्स का दुरुपयोग कैसे करें:
ये स्ट्रिंग शाब्दिक 32-बिट x86 मशीन कोड के बाइट्स हैं। 0xC3
है एक x86 ret
अनुदेश ।
आप सामान्य रूप से इन्हें हाथ से नहीं लिखेंगे, आप असेंबली भाषा में लिखेंगे और फिर एक असेंबलर का उपयोग nasm
करके इसे एक फ्लैट बाइनरी में इकट्ठा कर सकते हैं जिसे आप सी स्ट्रिंग शाब्दिक में हेक्सडंप करते हैं।
EAX रजिस्टर पर वर्तमान मूल्य लौटाता है
int eax = ((int(*)())("\xc3 <- This returns the value of the EAX register"))();
एक स्वैप फ़ंक्शन लिखें
int a = 10, b = 20;
((void(*)(int*,int*))"\x8b\x44\x24\x04\x8b\x5c\x24\x08\x8b\x00\x8b\x1b\x31\xc3\x31\xd8\x31\xc3\x8b\x4c\x24\x04\x89\x01\x8b\x4c\x24\x08\x89\x19\xc3 <- This swaps the values of a and b")(&a,&b);
प्रत्येक बार कुछ फ़ंक्शन को कॉल करते हुए 1000 के लिए एक फॉर-लूप काउंटर लिखें
((int(*)())"\x66\x31\xc0\x8b\x5c\x24\x04\x66\x40\x50\xff\xd3\x58\x66\x3d\xe8\x03\x75\xf4\xc3")(&function); // calls function with 1->1000
आप एक पुनरावर्ती फ़ंक्शन भी लिख सकते हैं जो 100 तक गिना जाता है
const char* lol = "\x8b\x5c\x24\x4\x3d\xe8\x3\x0\x0\x7e\x2\x31\xc0\x83\xf8\x64\x7d\x6\x40\x53\xff\xd3\x5b\xc3\xc3 <- Recursively calls the function at address lol.";
i = ((int(*)())(lol))(lol);
ध्यान दें कि संकलक स्ट्रिंग शाब्दिक को .rodata
अनुभाग (या .rdata
विंडोज पर) में रखता है , जो पाठ खंड के भाग के रूप में जुड़ा हुआ है (कार्यों के लिए कोड के साथ)।
टेक्स्ट सेगमेंट में Read + Exec अनुमति है, इसलिए स्ट्रिंग फ़ंक्शन को इंगित करने के लिए स्ट्रिंग शाब्दिक की आवश्यकता के बिना काम करता है mprotect()
या VirtualProtect()
सिस्टम कॉल जैसे कि आपको गतिशील रूप से आवंटित मेमोरी की आवश्यकता होगी। (या gcc -z execstack
एक त्वरित हैक के रूप में स्टैक + डेटा सेगमेंट + हीप एक्जीक्यूटेबल के साथ प्रोग्राम को लिंक करता है।)
इन्हें अलग करने के लिए, आप इसे बाइट्स पर एक लेबल लगाने के लिए संकलित कर सकते हैं, और एक डिस्सेम्बलर का उपयोग कर सकते हैं।
// at global scope
const char swap[] = "\x8b\x44\x24\x04\x8b\x5c\x24\x08\x8b\x00\x8b\x1b\x31\xc3\x31\xd8\x31\xc3\x8b\x4c\x24\x04\x89\x01\x8b\x4c\x24\x08\x89\x19\xc3 <- This swaps the values of a and b";
के साथ संकलन gcc -c -m32 foo.c
और असहमति के साथ objdump -D -rwC -Mintel
, हम असेंबली प्राप्त कर सकते हैं, और यह पता लगा सकते हैं कि यह कोड EBX (कॉल-संरक्षित रजिस्टर) को क्लोबबेरिंग द्वारा एबीआई का उल्लंघन करता है और आम तौर पर अक्षम है।
00000000 <swap>:
0: 8b 44 24 04 mov eax,DWORD PTR [esp+0x4] # load int *a arg from the stack
4: 8b 5c 24 08 mov ebx,DWORD PTR [esp+0x8] # ebx = b
8: 8b 00 mov eax,DWORD PTR [eax] # dereference: eax = *a
a: 8b 1b mov ebx,DWORD PTR [ebx]
c: 31 c3 xor ebx,eax # pointless xor-swap
e: 31 d8 xor eax,ebx # instead of just storing with opposite registers
10: 31 c3 xor ebx,eax
12: 8b 4c 24 04 mov ecx,DWORD PTR [esp+0x4] # reload a from the stack
16: 89 01 mov DWORD PTR [ecx],eax # store to *a
18: 8b 4c 24 08 mov ecx,DWORD PTR [esp+0x8]
1c: 89 19 mov DWORD PTR [ecx],ebx
1e: c3 ret
not shown: the later bytes are ASCII text documentation
they're not executed by the CPU because the ret instruction sends execution back to the caller
यह मशीन कोड (शायद) विंडोज, लिनक्स, ओएस एक्स, और इतने पर 32-बिट कोड में काम करेगा: उन सभी ओएस पर डिफ़ॉल्ट कॉलिंग कन्वेंशन रजिस्टरों में अधिक कुशलता के बजाय स्टैक पर आर्ग्स को पार करते हैं। लेकिन ईबीएक्स को सभी सामान्य कॉलिंग सम्मेलनों में कॉल-संरक्षित किया जाता है, इसलिए इसे बिना सहेजे / पुनर्स्थापित किए बिना एक स्क्रैच रजिस्टर के रूप में उपयोग करने से यह आसानी से कॉलर को दुर्घटनाग्रस्त कर सकता है।
फंक्शन पॉइंटर्स के लिए मेरा पसंदीदा उपयोग सस्ता और आसान पुनरावृत्तियों के रूप में है -
#include <stdio.h>
#define MAX_COLORS 256
typedef struct {
char* name;
int red;
int green;
int blue;
} Color;
Color Colors[MAX_COLORS];
void eachColor (void (*fp)(Color *c)) {
int i;
for (i=0; i<MAX_COLORS; i++)
(*fp)(&Colors[i]);
}
void printColor(Color* c) {
if (c->name)
printf("%s = %i,%i,%i\n", c->name, c->red, c->green, c->blue);
}
int main() {
Colors[0].name="red";
Colors[0].red=255;
Colors[1].name="blue";
Colors[1].blue=255;
Colors[2].name="black";
eachColor(printColor);
}
int (*cb)(void *arg, ...)
। पुनरावृत्त का मान भी मुझे जल्दी बंद कर देता है (यदि गैर-शून्य)।
एक बार मूल घोषणाकर्ता होने के बाद फ़ंक्शन पॉइंटर्स घोषित करना आसान हो जाता है:
ID
: आईडी एक है*D
: करने के लिए डी सूचकD(<parameters>)
: डी समारोह लेने <
मानकों >
लौटनेजबकि डी उन्हीं नियमों का उपयोग कर बनाया गया एक और घोषणाकर्ता है। अंत में, कहीं न कहीं, यह ID
(उदाहरण के लिए नीचे देखें) के साथ समाप्त होता है , जो घोषित इकाई का नाम है। आइए एक फ़ंक्शन को एक पॉइंटर पर ले जाने के लिए एक फंक्शन का निर्माण करने की कोशिश करें जो कुछ भी नहीं ले रहा है और इंट वापस लौट रहा है, और एक पॉइंटर को एक चार्ज में ले जा रहा है और इंट वापस आ रहा है। टाइप-डीफ़्स के साथ यह ऐसा है
typedef int ReturnFunction(char);
typedef int ParameterFunction(void);
ReturnFunction *f(ParameterFunction *p);
जैसा कि आप देख रहे हैं, यह बहुत आसान है इसे टाइपडिफ का उपयोग करके बनाना। बिना टाइपराइफ़ के, यह उपरोक्त घोषणा नियमों के साथ कठिन नहीं है, लगातार लागू किया जाता है। जैसा कि आप देखते हैं कि मैं सूचक बिंदु को याद करता हूं और फ़ंक्शन वापस लौटता है। यह घोषणा के बहुत बाईं ओर दिखाई देता है, और ब्याज की नहीं है: यह अंत में जोड़ा जाता है अगर कोई पहले से ही घोषणाकर्ता का निर्माण करता है। चलो करते हैं। इसका लगातार निर्माण करना, पहली चिंता - संरचना का उपयोग करके दिखाना [
और ]
:
function taking
[pointer to [function taking [void] returning [int]]]
returning
[pointer to [function taking [char] returning [int]]]
जैसा कि आप देखते हैं, एक एक के बाद एक घोषणाकर्ताओं को जोड़कर एक प्रकार का पूरी तरह से वर्णन कर सकता है। निर्माण दो तरह से किया जा सकता है। एक नीचे-ऊपर है, बहुत सही चीज (पत्तियों) के साथ शुरू होता है और पहचानकर्ता तक रास्ता काम करता है। दूसरा तरीका टॉप-डाउन है, जो पहचानकर्ता पर शुरू होता है, पत्तियों तक नीचे जाने का काम करता है। मैं दोनों तरह से दिखाऊंगा।
निर्माण दाईं ओर की चीज़ से शुरू होता है: लौटाई गई चीज़, जो कि चार ले रही फंक्शन है। घोषणाकर्ताओं को अलग रखने के लिए, मैं उन्हें नंबर देने जा रहा हूं:
D1(char);
चार पैरामीटर सीधे डाला, क्योंकि यह तुच्छ है। द्वारा सूचक को सूचक द्वारा प्रतिस्थापित D1
करके जोड़ा जा रहा है *D2
। ध्यान दें कि हमें कोष्ठक को चारों ओर लपेटना है *D2
। जिसे *-operator
फंक्शन-कॉल ऑपरेटर की पूर्ववर्ती स्थिति को देखकर जाना जा सकता है ()
। हमारे कोष्ठक के बिना, कंपाइलर इसे पढ़ेगा *(D2(char p))
। लेकिन यह *D2
निश्चित रूप से अब तक डी 1 की जगह नहीं होगी । कोष्ठकों को हमेशा घोषणाकर्ताओं के आसपास अनुमति दी जाती है। यदि आप वास्तव में उनमें से बहुत कुछ जोड़ते हैं, तो आप कुछ भी गलत नहीं करते हैं।
(*D2)(char);
वापसी प्रकार पूरा हो गया है! अब, आइए, वापसी कीD2
घोषणा करने वाले फ़ंक्शन डिक्लेरेटर फ़ंक्शन<parameters>
द्वारा प्रतिस्थापित करते हैं , जो D3(<parameters>)
कि हम अभी हैं।
(*D3(<parameters>))(char)
ध्यान दें कि किसी भी कोष्ठक की आवश्यकता नहीं है, क्योंकि हम इस समय एक फ़ंक्शन-घोषणाकर्ता बनना चाहते हैं D3
और सूचक नहीं। महान, केवल एक चीज बची है, इसके लिए पैरामीटर हैं। पैरामीटर ठीक उसी तरह से किया जाता है जैसे हमने रिटर्न टाइप किया है, बस char
द्वारा प्रतिस्थापित किया गया है void
। तो मैं इसे कॉपी करूँगा:
(*D3( (*ID1)(void)))(char)
मैंने बदल दिया D2
है ID1
, क्योंकि हम उस पैरामीटर के साथ समाप्त हो गए हैं (यह पहले से ही एक फ़ंक्शन के लिए एक संकेतक है - किसी अन्य घोषणाकर्ता की आवश्यकता नहीं है)। ID1
पैरामीटर का नाम होगा। अब, मैंने ऊपर बताया कि अंत में एक प्रकार का जोड़ा जाता है, जिसे उन सभी घोषणाकर्ता संशोधित करते हैं - प्रत्येक घोषणा के बहुत बाईं ओर दिखाई देने वाला। फ़ंक्शंस के लिए, वह रिटर्न प्रकार बन जाता है। पॉइंटर्स टू टाइप आदि के लिए ... यह दिलचस्प है जब टाइप के नीचे लिखा जाता है, तो यह विपरीत क्रम में दिखाई देगा, बिल्कुल दाईं ओर :) वैसे भी, इसे प्रतिस्थापित करने से पूर्ण घोषणा प्राप्त होती है। दोनों बार int
बेशक।
int (*ID0(int (*ID1)(void)))(char)
मैंने ID0
उस उदाहरण में फ़ंक्शन के पहचानकर्ता को बुलाया है ।
यह पहचानकर्ता में टाइप के विवरण में बहुत बाईं ओर शुरू होता है, उस घोषणाकर्ता को लपेटता है क्योंकि हम दाईं ओर से चलते हैं। फ़ंक्शन के साथ प्रारंभ करें जो कि रिटर्निंग पैरामीटर ले रहा है<
>
ID0(<parameters>)
विवरण में अगली बात ("लौटने के बाद") सूचक थी । आइए इसे शामिल करें:
*ID0(<parameters>)
फिर अगली चीज थी फंक्शनलन रिटर्निंग <
पैरामीटर>
। पैरामीटर एक साधारण चार है, इसलिए हम इसे फिर से सही में डालते हैं, क्योंकि यह वास्तव में तुच्छ है।
(*ID0(<parameters>))(char)
हमारे द्वारा जोड़े गए कोष्ठकों पर ध्यान दें, क्योंकि हम फिर से चाहते हैं कि *
पहले बांधें, और फिर द (char)
। अन्यथा इसे पढ़ा होगा समारोह लेने <
मापदंडों >
समारोह लौटने ... । कार्य, रिटर्निंग फ़ंक्शंस की अनुमति भी नहीं है।
अब हमें सिर्फ <
पैरामीटर लगाने की जरूरत है >
। मैं विचलन का एक छोटा संस्करण दिखाऊंगा, क्योंकि मुझे लगता है कि आप पहले से ही अब तक यह विचार कर चुके हैं कि यह कैसे करना है।
pointer to: *ID1
... function taking void returning: (*ID1)(void)
बस घोषणाकर्ताओं के int
सामने रखें जैसे हमने नीचे-ऊपर के साथ किया था, और हम समाप्त कर रहे हैं
int (*ID0(int (*ID1)(void)))(char)
क्या बॉटम-अप या टॉप-डाउन बेहतर है? मैं नीचे-ऊपर का उपयोग कर रहा हूं, लेकिन कुछ लोग टॉप-डाउन के साथ अधिक सहज हो सकते हैं। मुझे लगता है कि यह स्वाद की बात है। संयोग से, यदि आप उस घोषणा में सभी ऑपरेटरों को लागू करते हैं, तो आप एक इंट प्राप्त कर रहे हैं:
int v = (*ID0(some_function_pointer))(some_char);
यह C में घोषणाओं की एक अच्छी संपत्ति है: घोषणा यह दावा करती है कि यदि उन ऑपरेटरों को पहचानकर्ता का उपयोग करके अभिव्यक्ति में उपयोग किया जाता है, तो यह बहुत बाईं ओर के प्रकार का उत्पादन करता है। यह सरणियों के लिए भी ऐसा ही है।
आशा है आपको यह छोटा सा ट्यूटोरियल पसंद आया होगा! अब हम इससे लिंक कर सकते हैं जब लोग कार्यों के अजीब घोषणा सिंटैक्स के बारे में आश्चर्य करते हैं। मैंने यथासंभव कम सी इंटर्नल्स डालने की कोशिश की। इसमें चीजों को संपादित / ठीक करने के लिए स्वतंत्र महसूस करें।
जब आप अलग-अलग समय पर अलग-अलग फ़ंक्शंस चाहते हैं, या विकास के विभिन्न चरणों के लिए उपयोग करना बहुत आसान है। उदाहरण के लिए, मैं एक होस्ट कंप्यूटर पर एक एप्लिकेशन विकसित कर रहा हूं जिसमें एक कंसोल है, लेकिन सॉफ़्टवेयर की अंतिम रिलीज़ एक एनेट ज़ेडबोर्ड पर डाली जाएगी (जिसमें डिस्प्ले और कंसोल के लिए पोर्ट हैं, लेकिन उनकी आवश्यकता नहीं है / चाहते हैं अंतिम रिहाई)। इसलिए विकास के दौरान, मैं printf
स्थिति और त्रुटि संदेशों को देखने के लिए उपयोग करूंगा , लेकिन जब मैं पूरा हो जाता हूं, तो मैं कुछ भी मुद्रित नहीं करना चाहता। यहाँ मैंने क्या किया है:
// First, undefine all macros associated with version.h
#undef DEBUG_VERSION
#undef RELEASE_VERSION
#undef INVALID_VERSION
// Define which version we want to use
#define DEBUG_VERSION // The current version
// #define RELEASE_VERSION // To be uncommented when finished debugging
#ifndef __VERSION_H_ /* prevent circular inclusions */
#define __VERSION_H_ /* by using protection macros */
void board_init();
void noprintf(const char *c, ...); // mimic the printf prototype
#endif
// Mimics the printf function prototype. This is what I'll actually
// use to print stuff to the screen
void (* zprintf)(const char*, ...);
// If debug version, use printf
#ifdef DEBUG_VERSION
#include <stdio.h>
#endif
// If both debug and release version, error
#ifdef DEBUG_VERSION
#ifdef RELEASE_VERSION
#define INVALID_VERSION
#endif
#endif
// If neither debug or release version, error
#ifndef DEBUG_VERSION
#ifndef RELEASE_VERSION
#define INVALID_VERSION
#endif
#endif
#ifdef INVALID_VERSION
// Won't allow compilation without a valid version define
#error "Invalid version definition"
#endif
में version.c
मैं उपस्थित 2 फंक्शन प्रोटोटाइप को परिभाषित करूंगाversion.h
#include "version.h"
/*****************************************************************************/
/**
* @name board_init
*
* Sets up the application based on the version type defined in version.h.
* Includes allowing or prohibiting printing to STDOUT.
*
* MUST BE CALLED FIRST THING IN MAIN
*
* @return None
*
*****************************************************************************/
void board_init()
{
// Assign the print function to the correct function pointer
#ifdef DEBUG_VERSION
zprintf = &printf;
#else
// Defined below this function
zprintf = &noprintf;
#endif
}
/*****************************************************************************/
/**
* @name noprintf
*
* simply returns with no actions performed
*
* @return None
*
*****************************************************************************/
void noprintf(const char* c, ...)
{
return;
}
ध्यान दें कि फ़ंक्शन पॉइंटर को किस version.h
रूप में प्रोटोटाइप किया गया है
void (* zprintf)(const char *, ...);
जब इसे एप्लिकेशन में संदर्भित किया जाता है, तो यह इंगित करने वाले स्थान पर निष्पादित करना शुरू कर देगा, जिसे अभी तक परिभाषित नहीं किया गया है।
में version.c
, में नोटिस board_init()
समारोह जहां zprintf
एक अनूठा समारोह (जिसका कार्य हस्ताक्षर से मेल खाता है) असाइन किया गया है संस्करण के आधार पर कि में परिभाषित किया गया हैversion.h
zprintf = &printf;
zprintf डीबगिंग उद्देश्यों के लिए printf कॉल करता है
या
zprintf = &noprint;
zprintf सिर्फ लौटता है और अनावश्यक कोड नहीं चलाएगा
कोड चलाना इस तरह होगा:
#include "version.h"
#include <stdlib.h>
int main()
{
// Must run board_init(), which assigns the function
// pointer to an actual function
board_init();
void *ptr = malloc(100); // Allocate 100 bytes of memory
// malloc returns NULL if unable to allocate the memory.
if (ptr == NULL)
{
zprintf("Unable to allocate memory\n");
return 1;
}
// Other things to do...
return 0;
}
उपरोक्त कोड printf
डिबग मोड में उपयोग करेगा , या यदि रिलीज़ मोड में कुछ भी नहीं है। यह पूरी परियोजना के माध्यम से जाने और कोड को हटाने या हटाने से बहुत आसान है। मुझे जो कुछ करने की ज़रूरत है वह संस्करण को बदलना है version.h
और कोड बाकी काम करेगा!
फ़ंक्शन पॉइंटर आमतौर पर द्वारा परिभाषित किया जाता है typedef
, और इसका उपयोग परम और रिटर्न वैल्यू के रूप में किया जाता है।
उपरोक्त उत्तर पहले से ही बहुत कुछ समझाते हैं, मैं सिर्फ एक पूर्ण उदाहरण देता हूं:
#include <stdio.h>
#define NUM_A 1
#define NUM_B 2
// define a function pointer type
typedef int (*two_num_operation)(int, int);
// an actual standalone function
static int sum(int a, int b) {
return a + b;
}
// use function pointer as param,
static int sum_via_pointer(int a, int b, two_num_operation funp) {
return (*funp)(a, b);
}
// use function pointer as return value,
static two_num_operation get_sum_fun() {
return ∑
}
// test - use function pointer as variable,
void test_pointer_as_variable() {
// create a pointer to function,
two_num_operation sum_p = ∑
// call function via pointer
printf("pointer as variable:\t %d + %d = %d\n", NUM_A, NUM_B, (*sum_p)(NUM_A, NUM_B));
}
// test - use function pointer as param,
void test_pointer_as_param() {
printf("pointer as param:\t %d + %d = %d\n", NUM_A, NUM_B, sum_via_pointer(NUM_A, NUM_B, &sum));
}
// test - use function pointer as return value,
void test_pointer_as_return_value() {
printf("pointer as return value:\t %d + %d = %d\n", NUM_A, NUM_B, (*get_sum_fun())(NUM_A, NUM_B));
}
int main() {
test_pointer_as_variable();
test_pointer_as_param();
test_pointer_as_return_value();
return 0;
}
C में फ़ंक्शन पॉइंटर्स के लिए एक बड़ा उपयोग रन-टाइम में चयनित फ़ंक्शन को कॉल करना है। उदाहरण के लिए, सी रन-टाइम लाइब्रेरी में दो रूटीन हैं, qsort
और bsearch
, जो एक फ़ंक्शन को एक पॉइंटर लेते हैं जिसे दो वस्तुओं को क्रमबद्ध करने के लिए कहा जाता है; यह आपको किसी भी मानदंड के आधार पर क्रमबद्ध या खोज करने की अनुमति देता है, जिसका आप उपयोग करना चाहते हैं।
एक बहुत ही बुनियादी उदाहरण, अगर कोई एक फ़ंक्शन है जिसे print(int x, int y)
बदले में फ़ंक्शन (या तो , add()
या sub()
जो एक ही प्रकार के हैं) को कॉल करने की आवश्यकता हो सकती है तो हम क्या करेंगे, हम फ़ंक्शन को एक फ़ंक्शन पॉइंटर जोड़ देंगे print()
जैसा कि नीचे दिखाया गया है :
#include <stdio.h>
int add()
{
return (100+10);
}
int sub()
{
return (100-10);
}
void print(int x, int y, int (*func)())
{
printf("value is: %d\n", (x+y+(*func)()));
}
int main()
{
int x=100, y=200;
print(x,y,add);
print(x,y,sub);
return 0;
}
आउटपुट है:
मूल्य है: 410
मूल्य है: 390
स्क्रैच फ़ंक्शन से शुरू होने वाले कुछ मेमोरी एड्रेस हैं जहां से वे निष्पादित करना शुरू करते हैं। असेंबली लैंग्वेज में उन्हें कहा जाता है ("फ़ंक्शन का मेमोरी एड्रेस")
1. पहले आप कार्य करने के लिए एक सूचक घोषित करने की आवश्यकता है 2. वांछित समारोह का पता दर्ज करें
**** नोट-> फ़ंक्शन समान प्रकार के होने चाहिए ****
यह सरल कार्यक्रम हर बात पर प्रकाश डालेगा।
#include<stdio.h>
void (*print)() ;//Declare a Function Pointers
void sayhello();//Declare The Function Whose Address is to be passed
//The Functions should Be of Same Type
int main()
{
print=sayhello;//Addressof sayhello is assigned to print
print();//print Does A call To The Function
return 0;
}
void sayhello()
{
printf("\n Hello World");
}
उसके बाद देखते हैं कि कैसे मशीन उन्हें देती है। 32 बिट आर्किटेक्चर में उपरोक्त प्रोग्राम की मशीन निर्देश की झलक।
लाल निशान क्षेत्र दिखा रहा है कि पते का आदान-प्रदान कैसे किया जा रहा है और बाज में संग्रहीत किया जा रहा है। तब उनका ईगल पर कॉल निर्देश है। eax में फ़ंक्शन का वांछित पता होता है।
एक फंक्शन पॉइंटर एक वैरिएबल है जिसमें एक फंक्शन का पता होता है। चूंकि यह एक पॉइंटर वैरिएबल है, हालांकि कुछ प्रतिबंधित प्रॉपर्टीज के साथ, आप इसे बहुत ज्यादा इस्तेमाल कर सकते हैं जैसे कि आप डेटा स्ट्रक्चर्स में कोई अन्य पॉइंटर वैरिएबल है।
एकमात्र अपवाद जिसके बारे में मैं सोच सकता हूं, वह फ़ंक्शन पॉइंटर को एक मान के अलावा किसी अन्य चीज़ की ओर इशारा करता है। किसी फ़ंक्शन पॉइंटर को बढ़ाकर या घटाकर पॉइंटर अंकगणित करना या किसी फ़ंक्शन पॉइंटर को ऑफ़सेट जोड़ना / घटाना वास्तव में किसी भी उपयोगिता का नहीं है क्योंकि फ़ंक्शन पॉइंटर केवल एक चीज की ओर इशारा करता है, एक फ़ंक्शन का प्रवेश बिंदु।
एक फंक्शन पॉइंटर वैरिएबल का आकार, वैरिएबल के कब्जे वाले बाइट्स की संख्या, अंतर्निहित आर्किटेक्चर, जैसे x32 या x64 या जो भी हो, के आधार पर भिन्न हो सकती है।
फंक्शन पॉइंटर वैरिएबल के लिए डिक्लेरेशन को सी कंपाइलर के लिए फंक्शन डिक्लेरेशन के तौर पर उसी तरह की इंफॉर्मेशन बताने की जरूरत होती है, जिस तरह के चेक को वह आमतौर पर करता है। यदि आप फ़ंक्शन पॉइंटर की घोषणा / परिभाषा में एक पैरामीटर सूची निर्दिष्ट नहीं करते हैं, तो सी कंपाइलर मापदंडों के उपयोग की जांच करने में सक्षम नहीं होगा। ऐसे मामले हैं जब जाँच की यह कमी उपयोगी हो सकती है लेकिन बस याद रखें कि सुरक्षा जाल हटा दिया गया है।
कुछ उदाहरण:
int func (int a, char *pStr); // declares a function
int (*pFunc)(int a, char *pStr); // declares or defines a function pointer
int (*pFunc2) (); // declares or defines a function pointer, no parameter list specified.
int (*pFunc3) (void); // declares or defines a function pointer, no arguments.
पहले दो घोषणाएँ कुछ इस तरह हैं:
func
एक फ़ंक्शन है जो एक int
और एक लेता है और एक char *
रिटर्न देता हैint
pFunc
एक फंक्शन पॉइंटर है, जिसे एक फंक्शन का पता सौंपा जाता है, जो int
a char *
और a को रिटर्न करता हैint
तो ऊपर से हमारे पास एक स्रोत रेखा हो सकती है जिसमें फ़ंक्शन func()
का पता फ़ंक्शन पॉइंटर वैरिएबल pFunc
को सौंपा जाता है pFunc = func;
।
फ़ंक्शन पॉइंटर घोषणा / परिभाषा के साथ उपयोग किए गए वाक्यविन्यास पर ध्यान दें जिसमें प्राकृतिक ऑपरेटर पूर्ववर्ती नियमों को पार करने के लिए कोष्ठक का उपयोग किया जाता है।
int *pfunc(int a, char *pStr); // declares a function that returns int pointer
int (*pFunc)(int a, char *pStr); // declares a function pointer that returns an int
कई अलग-अलग उपयोग उदाहरण
फ़ंक्शन पॉइंटर के उपयोग के कुछ उदाहरण:
int (*pFunc) (int a, char *pStr); // declare a simple function pointer variable
int (*pFunc[55])(int a, char *pStr); // declare an array of 55 function pointers
int (**pFunc)(int a, char *pStr); // declare a pointer to a function pointer variable
struct { // declare a struct that contains a function pointer
int x22;
int (*pFunc)(int a, char *pStr);
} thing = {0, func}; // assign values to the struct variable
char * xF (int x, int (*p)(int a, char *pStr)); // declare a function that has a function pointer as an argument
char * (*pxF) (int x, int (*p)(int a, char *pStr)); // declare a function pointer that points to a function that has a function pointer as an argument
आप फ़ंक्शन पॉइंटर की परिभाषा में चर लंबाई पैरामीटर सूची का उपयोग कर सकते हैं।
int sum (int a, int b, ...);
int (*psum)(int a, int b, ...);
या आप एक पैरामीटर सूची बिल्कुल भी निर्दिष्ट नहीं कर सकते। यह उपयोगी हो सकता है लेकिन यह सी कंपाइलर द्वारा प्रदान की गई तर्क सूची पर जांच करने के अवसर को समाप्त कर देता है।
int sum (); // nothing specified in the argument list so could be anything or nothing
int (*psum)();
int sum2(void); // void specified in the argument list so no parameters when calling this function
int (*psum2)(void);
सी स्टाइल की कास्ट
आप फंक्शन पॉइंटर्स के साथ C स्टाइल कास्ट्स का उपयोग कर सकते हैं। हालाँकि, इस बात से अवगत रहें कि C संकलक जाँच के बारे में शिथिल हो सकता है या त्रुटियों के बजाय चेतावनी दे सकता है।
int sum (int a, char *b);
int (*psplsum) (int a, int b);
psplsum = sum; // generates a compiler warning
psplsum = (int (*)(int a, int b)) sum; // no compiler warning, cast to function pointer
psplsum = (int *(int a, int b)) sum; // compiler error of bad cast generated, parenthesis are required.
फंक्शन पॉइंटर की समानता से तुलना करें
आप देख सकते हैं कि एक फ़ंक्शन पॉइंटर एक if
बयान का उपयोग करके एक विशेष फ़ंक्शन पते के बराबर है, हालांकि मुझे यकीन नहीं है कि यह कितना उपयोगी होगा। अन्य तुलना संचालकों की उपयोगिता भी कम प्रतीत होगी।
static int func1(int a, int b) {
return a + b;
}
static int func2(int a, int b, char *c) {
return c[0] + a + b;
}
static int func3(int a, int b, char *x) {
return a + b;
}
static char *func4(int a, int b, char *c, int (*p)())
{
if (p == func1) {
p(a, b);
}
else if (p == func2) {
p(a, b, c); // warning C4047: '==': 'int (__cdecl *)()' differs in levels of indirection from 'char *(__cdecl *)(int,int,char *)'
} else if (p == func3) {
p(a, b, c);
}
return c;
}
फंक्शन पॉइंटर्स का एक ऐरे
और यदि आप फ़ंक्शन पॉइंट की एक सरणी रखना चाहते हैं, जिसमें से प्रत्येक तत्व में तर्क सूची में अंतर है, तो आप एक फ़ंक्शन पॉइंटर को तर्क सूची के साथ परिभाषित कर सकते हैं अनिर्दिष्ट ( void
जिसका अर्थ है कि कोई तर्क नहीं है लेकिन सिर्फ अनिर्दिष्ट) निम्नलिखित जैसे कुछ आप सी संकलक से चेतावनी देख सकते हैं। यह भी एक फ़ंक्शन पॉइंटर पैरामीटर फ़ंक्शन के लिए काम करता है:
int(*p[])() = { // an array of function pointers
func1, func2, func3
};
int(**pp)(); // a pointer to a function pointer
p[0](a, b);
p[1](a, b, 0);
p[2](a, b); // oops, left off the last argument but it compiles anyway.
func4(a, b, 0, func1);
func4(a, b, 0, func2); // warning C4047: 'function': 'int (__cdecl *)()' differs in levels of indirection from 'char *(__cdecl *)(int,int,char *)'
func4(a, b, 0, func3);
// iterate over the array elements using an array index
for (i = 0; i < sizeof(p) / sizeof(p[0]); i++) {
func4(a, b, 0, p[i]);
}
// iterate over the array elements using a pointer
for (pp = p; pp < p + sizeof(p)/sizeof(p[0]); pp++) {
(*pp)(a, b, 0); // pointer to a function pointer so must dereference it.
func4(a, b, 0, *pp); // pointer to a function pointer so must dereference it.
}
फंक्शन पॉइंटर्स के साथ namespace
ग्लोबल का उपयोग करते हुए सी स्टाइलstruct
आप static
किसी फ़ंक्शन को निर्दिष्ट करने के लिए कीवर्ड का उपयोग कर सकते हैं जिसका नाम फ़ाइल स्कोप है और फिर इसे namespace
C ++ की कार्यक्षमता के समान कुछ प्रदान करने के तरीके के रूप में एक वैश्विक चर पर असाइन करें ।
एक हेडर फ़ाइल में एक ऐसी संरचना को परिभाषित करते हैं जो एक वैश्विक चर के साथ हमारा नाम स्थान होगा जो इसका उपयोग करता है।
typedef struct {
int (*func1) (int a, int b); // pointer to function that returns an int
char *(*func2) (int a, int b, char *c); // pointer to function that returns a pointer
} FuncThings;
extern const FuncThings FuncThingsGlobal;
फिर सी स्रोत फ़ाइल में:
#include "header.h"
// the function names used with these static functions do not need to be the
// same as the struct member names. It's just helpful if they are when trying
// to search for them.
// the static keyword ensures these names are file scope only and not visible
// outside of the file.
static int func1 (int a, int b)
{
return a + b;
}
static char *func2 (int a, int b, char *c)
{
c[0] = a % 100; c[1] = b % 50;
return c;
}
const FuncThings FuncThingsGlobal = {func1, func2};
इसके बाद फ़ंक्शन को एक्सेस करने के लिए ग्लोबल स्ट्रक्चर वेरिएबल का पूरा नाम और मेंबर का नाम निर्दिष्ट करके उपयोग किया जाएगा। const
संशोधक वैश्विक इतने पर प्रयोग किया जाता है कि यह दुर्घटना से नहीं बदला जा सकता।
int abcd = FuncThingsGlobal.func1 (a, b);
फ़ंक्शन पॉइंटर्स के अनुप्रयोग क्षेत्र
डीएलएल लाइब्रेरी घटक सी शैली के namespace
दृष्टिकोण के समान कुछ कर सकता है जिसमें एक लाइब्रेरी इंटरफ़ेस में एक फैक्ट्री विधि से एक विशेष लाइब्रेरी इंटरफ़ेस का अनुरोध किया जाता है जो एक struct
फ़ंक्शन फ़ंक्शन के निर्माण का समर्थन करता है .. यह लाइब्रेरी इंटरफ़ेस अनुरोधित DLL संस्करण को लोड करता है, बनाता है आवश्यक फ़ंक्शन बिंदुओं के साथ एक संरचना, और फिर उपयोग के लिए अनुरोध करने वाले कॉलर को संरचना लौटाता है।
typedef struct {
HMODULE hModule;
int (*Func1)();
int (*Func2)();
int(*Func3)(int a, int b);
} LibraryFuncStruct;
int LoadLibraryFunc LPCTSTR dllFileName, LibraryFuncStruct *pStruct)
{
int retStatus = 0; // default is an error detected
pStruct->hModule = LoadLibrary (dllFileName);
if (pStruct->hModule) {
pStruct->Func1 = (int (*)()) GetProcAddress (pStruct->hModule, "Func1");
pStruct->Func2 = (int (*)()) GetProcAddress (pStruct->hModule, "Func2");
pStruct->Func3 = (int (*)(int a, int b)) GetProcAddress(pStruct->hModule, "Func3");
retStatus = 1;
}
return retStatus;
}
void FreeLibraryFunc (LibraryFuncStruct *pStruct)
{
if (pStruct->hModule) FreeLibrary (pStruct->hModule);
pStruct->hModule = 0;
}
और इस रूप में इस्तेमाल किया जा सकता है:
LibraryFuncStruct myLib = {0};
LoadLibraryFunc (L"library.dll", &myLib);
// ....
myLib.Func1();
// ....
FreeLibraryFunc (&myLib);
कोड के लिए एक सार हार्डवेयर परत को परिभाषित करने के लिए उसी दृष्टिकोण का उपयोग किया जा सकता है जो अंतर्निहित हार्डवेयर के एक विशेष मॉडल का उपयोग करता है। फ़ंक्शन पॉइंटर्स हार्डवेयर विशिष्ट कार्यक्षमता प्रदान करने के लिए फ़ैक्टरी द्वारा हार्डवेयर विशिष्ट फ़ंक्शंस से भरे जाते हैं जो अमूर्त हार्डवेयर मॉडल में निर्दिष्ट फ़ंक्शन को लागू करते हैं। इसका उपयोग सॉफ्टवेयर द्वारा उपयोग की जाने वाली एक सार हार्डवेयर परत प्रदान करने के लिए किया जा सकता है, जो विशिष्ट हार्डवेयर फ़ंक्शन इंटरफ़ेस प्राप्त करने के लिए फ़ैक्टरी फ़ंक्शन को कॉल करता है, फिर विशिष्ट लक्ष्य के बारे में कार्यान्वयन विवरण जानने की आवश्यकता के बिना अंतर्निहित हार्डवेयर के लिए कार्य करने के लिए दिए गए फ़ंक्शन पॉइंटर्स का उपयोग करता है। ।
प्रतिनिधि बिंदु, प्रतिनिधि, हैंडलर और कॉलबैक बनाने के लिए
आप फ़ंक्शन पॉइंटर्स का उपयोग कुछ कार्य या कार्यक्षमता को सौंपने के तरीके के रूप में कर सकते हैं। C में क्लासिक उदाहरण मानक सी लाइब्रेरी फ़ंक्शंस के साथ तुलनात्मक प्रतिनिधि फ़ंक्शन पॉइंटर का उपयोग किया जाता है qsort()
और bsearch()
आइटमों की सूची को सॉर्ट करने के लिए या आइटमों की एक सॉर्ट की गई सूची पर बाइनरी खोज का प्रदर्शन करने के लिए कोलाजेशन ऑर्डर प्रदान करता है। तुलना फ़ंक्शन प्रतिनिधि, सॉर्ट या बाइनरी खोज में उपयोग किए जाने वाले कोलाज़ एल्गोरिथ्म को निर्दिष्ट करता है।
एक अन्य उपयोग C ++ मानक टेम्प्लेट लाइब्रेरी कंटेनर में एल्गोरिथ्म लागू करने के समान है।
void * ApplyAlgorithm (void *pArray, size_t sizeItem, size_t nItems, int (*p)(void *)) {
unsigned char *pList = pArray;
unsigned char *pListEnd = pList + nItems * sizeItem;
for ( ; pList < pListEnd; pList += sizeItem) {
p (pList);
}
return pArray;
}
int pIncrement(int *pI) {
(*pI)++;
return 1;
}
void * ApplyFold(void *pArray, size_t sizeItem, size_t nItems, void * pResult, int(*p)(void *, void *)) {
unsigned char *pList = pArray;
unsigned char *pListEnd = pList + nItems * sizeItem;
for (; pList < pListEnd; pList += sizeItem) {
p(pList, pResult);
}
return pArray;
}
int pSummation(int *pI, int *pSum) {
(*pSum) += *pI;
return 1;
}
// source code and then lets use our function.
int intList[30] = { 0 }, iSum = 0;
ApplyAlgorithm(intList, sizeof(int), sizeof(intList) / sizeof(intList[0]), pIncrement);
ApplyFold(intList, sizeof(int), sizeof(intList) / sizeof(intList[0]), &iSum, pSummation);
एक अन्य उदाहरण जीयूआई स्रोत कोड के साथ है जिसमें एक विशेष घटना के लिए एक हैंडलर एक फ़ंक्शन पॉइंटर प्रदान करके पंजीकृत होता है जिसे वास्तव में घटना होने पर कहा जाता है। अपने संदेश मानचित्रों के साथ Microsoft MFC फ्रेमवर्क विंडोज़ संदेशों को संभालने के लिए कुछ इसी तरह का उपयोग करता है जो एक विंडो या थ्रेड पर वितरित किए जाते हैं।
अतुल्यकालिक फ़ंक्शन जिन्हें कॉलबैक की आवश्यकता होती है, वे ईवेंट हैंडलर के समान हैं। एसिंक्रोनस फ़ंक्शन का उपयोगकर्ता कुछ कार्रवाई शुरू करने के लिए एसिंक्रोनस फ़ंक्शन को कॉल करता है और एक फ़ंक्शन पॉइंटर प्रदान करता है जिसे कार्रवाई पूरा होने पर एसिंक्रोनस फ़ंक्शन कॉल करेगा। इस मामले में यह घटना अतुल्यकालिक फ़ंक्शन है जो अपना कार्य पूरा कर रही है।
चूंकि फंक्शन पॉइंटर्स अक्सर कॉलबैक टाइप किए जाते हैं, इसलिए आप टाइप सेफ कॉलबैक पर एक नज़र डालना चाहते हैं । यही बात एंट्री पॉइंट पर लागू होती है, ऐसे फंक्शंस के आदि जो कॉलबैक नहीं हैं।
C एक ही समय में काफी चंचल और क्षमाशील है :)