हाउ वी गॉट हियर
फ़ंक्शन पॉइंट घोषित करने के लिए सी सिंटैक्स का उपयोग दर्पण उपयोग के लिए किया गया था। इस तरह से एक नियमित कार्य घोषणा पर विचार करें <math.h>:
double round(double number);
एक पॉइंट वैरिएबल के लिए आप इसे टाइप सुरक्षा के साथ असाइन कर सकते हैं
fp = round;
आपको उस fpबिंदु को इस तरह से चर घोषित करना होगा:
double (*fp)(double number);
इसलिए आपको केवल यह देखना है कि आप फ़ंक्शन का उपयोग कैसे करेंगे, और उस फ़ंक्शन का नाम एक पॉइंटर संदर्भ के साथ roundबदल देगा , जिसमें*fp । हालाँकि, आपको एक अतिरिक्त सेट की आवश्यकता है, जो यह कहता है कि यह थोड़ा गड़बड़ है।
यकीनन, यह मूल C में आसान हुआ करता था, जिसमें फ़ंक्शन सिग्नेचर भी नहीं था, लेकिन चलो वापस वहाँ नहीं जाते हैं, ठीक है?
जिस स्थान पर यह विशेष रूप से बुरा हो जाता है, वह यह पता लगाता है कि एक फ़ंक्शन को कैसे घोषित किया जाए जो या तो एक तर्क के रूप में लेता है या एक फ़ंक्शन को पॉइंटर लौटाता है, या दोनों।
यदि आप एक समारोह था:
void myhandler(int signo);
आप इसे सिग्नल फ़ंक्शन (3) में इस तरह से पास कर सकते हैं:
signal(SIGHUP, myhandler);
या यदि आप पुराने हैंडलर को रखना चाहते हैं, तो
old_handler = signal(SIGHUP, new_handler);
जो बहुत आसान है। क्या बहुत आसान है - और न ही सुंदर, न ही आसान - घोषणाओं को सही मान रहा है।
signal(int signo, ???)
ठीक है, आप बस अपने फ़ंक्शन घोषणा पर वापस जाएं और एक बिंदु संदर्भ के लिए नाम स्वैप करें:
signal(int sendsig, void (*hisfunc)(int gotsig));
क्योंकि आप घोषणा नहीं कर रहे हैं gotsig, तो आपको यह पढ़ने में आसानी हो सकती है कि क्या आप छोड़ देते हैं:
signal(int sendsig, void (*hisfunc)(int));
या शायद नहीं। :(
सिवाय इसके कि यह काफी अच्छा नहीं है, क्योंकि सिग्नल (3) पुराने हैंडलर को भी लौटाता है, जैसे:
old_handler = signal(SIGHUP, new_handler);
तो अब आपको यह पता लगाना है कि उन सभी को कैसे घोषित किया जाए।
void (*old_handler)(int gotsig);
उस चर के लिए पर्याप्त है जिसे आप असाइन करने जा रहे हैं। ध्यान दें कि आप वास्तव में gotsigयहां केवल घोषणा नहीं कर रहे हैं old_handler। तो यह वास्तव में पर्याप्त है:
void (*old_handler)(int);
यह हमें संकेत के लिए एक सही परिभाषा देता है (3):
void (*signal(int signo, void (*handler)(int)))(int);
बचाव के लिए टंकण
इस समय तक, मुझे लगता है कि हर कोई सहमत होगा कि यह एक गड़बड़ है। कभी-कभी अपने अमूर्त नाम देना बेहतर होता है; अक्सर, वास्तव में। अधिकार के साथ typedef, यह समझना बहुत आसान हो जाता है:
typedef void (*sig_t) (int);
अब आपका अपना हैंडलर वैरिएबल बन जाता है
sig_t old_handler, new_handler;
और संकेत के लिए आपकी घोषणा (3) बस हो जाती है
sig_t signal(int signo, sig_t handler);
जो अचानक समझ में आता है। * से छुटकारा पाने से कुछ भ्रमित कोष्ठकों से भी छुटकारा मिल जाता है (और वे कहते हैं कि परेंस हमेशा चीजों को समझने में आसान बनाते हैं - हाह!)। आपका उपयोग अभी भी समान है:
old_handler = signal(SIGHUP, new_handler);
लेकिन अब आप के लिए घोषणाओं को समझने का मौका है old_handler, new_handlerऔर यहां तक किsignal जब आप पहली बार उन्हें या जरूरत का सामना उन्हें लिखने के लिए।
निष्कर्ष
बहुत कम सी प्रोग्रामर, यह पता चला है, संदर्भ सामग्री के परामर्श के बिना इन चीजों के लिए सही घोषणाओं को तैयार करने में सक्षम हैं।
मुझे पता है, क्योंकि हम एक बार कर्नेल और डिवाइस ड्राइवर का काम करने वाले लोगों के लिए हमारे साक्षात्कार के सवालों पर बहुत सवाल करते थे। :) ज़रूर, हमने बहुत से उम्मीदवारों को खो दिया, जिस तरह से वे दुर्घटनाग्रस्त हो गए और व्हाइटबोर्ड पर जल गए। लेकिन हमने उन लोगों को काम पर रखने से भी परहेज किया जिन्होंने दावा किया था कि उन्हें इस क्षेत्र में पिछले अनुभव थे लेकिन वास्तव में वह काम नहीं कर सके।
इस व्यापक कठिनाई के कारण, हालांकि, यह शायद समझदार नहीं है, लेकिन वास्तव में उन सभी घोषणाओं के बारे में जाने का एक तरीका है जो अब आपको ट्रिपल-अल्फा गीक प्रोग्रामर होने की आवश्यकता नहीं है, जो केवल इस का उपयोग करने के लिए तीन से ऊपर बैठे हैं। आराम से बात करना।
f :: (Int -> Int -> Int) -> Int -> Int