2009-04-16 13 views
5

निम्नलिखित घोषणाओं है:प्रश्न समारोह सूचक सी में बारे में

void qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *)); 
int numcmp(char *, char *); 
int strcmp(char *s, char *t); 
फिर

, कहीं कार्यक्रम में वहाँ निम्नलिखित कॉल है:

qsort((void**) lineptr, 0, nlines-1, 
        (int (*)(void*,void*))(numeric ? numcmp : strcmp)); 

(पहले तीन तर्कों पर ध्यान न दें और numeric) ।

मैं पूछता हूँ यह है कि क्या:

(int (*)(void*,void*))(numeric ? numcmp : strcmp) 

मैं समझता हूँ कि qsort के रूप में यह 4 तर्क है, लेकिन कैसे क्या संतुष्ट ऊपर लिखा है कि "समारोह है कि दो void संकेत हो जाता है और एक int रिटर्न के लिए सूचक" एक उम्मीद कर रही है? ऐसा लगता है कि मुझे कुछ प्रकार की कास्ट पसंद है क्योंकि यह दो कोष्ठक से बना है, लेकिन यह एक बहुत ही अजीब कलाकार होगा। क्योंकि यह एक फ़ंक्शन लेता है और यह फ़ंक्शन "फ़ंक्शन करने के लिए पॉइंटर बनाता है जो दो void पॉइंटर्स प्राप्त करता है और int देता है"। जो अर्थहीन है।
(मैंने नियम का पालन किया है कि एक प्रकार type एक चर वैरिएबल को उस प्रकार के चर को बढ़ावा देने से पहले)।

तो मुझे लगता है कि मुझे यह गलत लगता है, शायद कोई मुझे बता सकता है कि इसे कैसे पढ़ा जाए, आदेश क्या है?

+0

यह अर्थहीन क्यों है? यह मेरे लिए ठीक लग रहा है। – cadrian

+0

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

+0

एक फंक्शन के लिए एक पॉइंटर एक * फ़ंक्शन पॉइंटर * है ... जो फ़ंक्शन के रूप में पूरी तरह उपयोग करने योग्य है, धन्यवाद ... – Varkhan

उत्तर

4

यहां क्या हो रहा है वास्तव में एक कलाकार है। आइए एक सेकंड के लिए टर्नरी को अनदेखा करें और दिखाएं कि numcmp हमेशा प्रयोग किया जाता है। इस सवाल के प्रयोजन के लिए, यह वास्तव में काम करता है

(int (*)(int*,int*)) 

के लिए इस ठीक से qsort में प्रयोग की जाने वाली यह शून्य की जरूरत है आदेश में तो अगर आप सांख्यिक के प्रकार को देखने के सी में समारोह संकेत के रूप में कार्य कर सकते हैं मापदंडों। चूंकि यहां सभी प्रकार के पैरामीटर और रिटर्न प्रकारों के संबंध में समान आकार होते हैं, इसलिए दूसरे के लिए प्रतिस्थापित करना संभव है। कंपाइलर को खुश करने के लिए इसकी जरूरत है।

(int (*)(void*,void*))(numcmp) 
+1

आप numcmp के बारे में बात करना चाहते हैं, न कि संख्यात्मक –

+0

@ ब्रायन, धन्यवाद। मैंने – JaredPar

+0

पोस्ट को अपडेट किया है आपने लिखा है कि "संख्यात्मक प्रकार वास्तव में है (int (*) (int *, int *))"। मुझे यह नहीं मिला। टर्नरी अभिव्यक्ति का प्रकार एक फ़ंक्शन है और आपने जो लिखा है वह एक फ़ंक्शन के लिए सूचक है। –

0

आपका तर्क सही है मुझे लगता है। यह वास्तव में "काम करने के लिए सूचक है जो दो शून्य पॉइंटर्स प्राप्त करता है और एक int देता है" जो विधि हस्ताक्षर द्वारा आवश्यक प्रकार है।

4

आप चाल यहाँ नहीं छूटा है - भाग

(numeric ? numcmp : strcmp) 

त्रिगुट ऑपरेटर उपयोग कर रहा है का चयन करने के जो समारोह qsort के अंदर बुलाया जा रहा है। यदि डेटा संख्यात्मक है, तो यह numcmp का उपयोग करता है। यदि नहीं, तो यह strcmp का उपयोग करता है। एक और पठनीय कार्यान्वयन इस तरह दिखेगा:

int (*comparison_function)(void*,void*) = 
    (int (*)(void*,void*))(numeric ? numcmp : strcmp); 
qsort((void**) lineptr, 0, nlines-1, comparison_function); 
+2

मुझे वास्तव में विश्वास नहीं है कि कोई और पठनीय :) –

+0

@ सेन ब्राइट: मैं वास्तव में ऐसा नहीं सोचता * यह * पढ़ने योग्य बनाने का एक तरीका है। इसमें एकमात्र लाभ यह है कि यह एक परिवर्तनीय नाम (compar_function) का उपयोग करता है जो यह बताने की कोशिश करता है कि क्या हो रहा है। –

+0

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

0

दोनों numcmp और strcmp कार्य करता है जो पैरामीटर के रूप में दो char* लेने के लिए संकेत दिए गए हैं और एक int देता है। qsort दिनचर्या एक सूचक को एक फ़ंक्शन की अपेक्षा करता है जो पैरामीटर के रूप में दो void* लेता है और int देता है। इसलिए कलाकार। यह सुरक्षित है, क्योंकि void* एक सामान्य सूचक के रूप में कार्य करता है।अब, घोषणा पढ़ने के लिए पर: चलो लेते हैं अपनेstrcmp की घोषणा:

int strcmp(char *, char *); 

संकलक इसे पढ़ता है के रूप में strcmp वास्तव में है:

int (strcmp)(char *, char *) 

एक समारोह (एक करने के लिए एक सूचक को खस्ताहाल ज्यादातर मामलों में कार्य) जो दो char * तर्क लेता है। जब आप किसी अन्य समारोह कास्ट करने के लिए strcmp के लिए संगत आप प्रकार को कास्ट करने के लिए के रूप में ऊपर का उपयोग करेंगे होने की जरूरत है,

int (*)(char *, char *) 

इसलिए: सूचक strcmp के प्रकार इसलिए है।

इसी तरह, qsort के तुलनात्मक तर्क के बाद दो void * एस और इस प्रकार अजीब कास्ट लेता है!

4

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

1

शायद मैं इसे इस तरह से लिखा होगा:

typedef int (*PFNCMP)(void *, void *); 

PFNCMP comparison_function; 

if (numeric) 
{ 
    comparison_function = numcmp; 
} 
else 
{ 
    comparison_function = strcmp; 
} 

qsort((void**) lineptr, 0, nlines-1, comparison_function); 

प्रश्न में उदाहरण के एक स्पष्ट मामला है।

3

ध्यान दें कि qsort() की मानक परिभाषा const में शामिल हैं:

void qsort(void *base, size_t nmemb, size_t size, 
      int (*compar)(const void *, const void *)); 

ध्यान दें कि स्ट्रिंग तुलनित्र दो 'char **' मूल्यों, नहीं 'char *' मान दिया जाता है।

मैं अपने तुलनाकारक लिखें ताकि डाले बुला कोड में अनावश्यक हैं:

#include <stdlib.h> /* qsort() */ 
#include <string.h> /* strcmp() */ 

int num_cmp(const void *v1, const void *v2) 
{ 
    int i1 = *(const int *)v1; 
    int i2 = *(const int *)v2; 
    if (i1 < i2) 
     return -1; 
    else if (i1 > i2) 
     return +1; 
    else 
     return 0; 
} 

int str_cmp(const void *v1, const void *v2) 
{ 
    const char *s1 = *(const char **)v1; 
    const char *s2 = *(const char **)v2; 
    return(strcmp(s1, s2)); 
} 

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

मैंने लिखा दो कार्य मानक qsort() द्वारा आवश्यक फ़ंक्शन प्रोटोटाइप से मेल खाते हैं। फ़ंक्शंस का पालन नहीं करते समय फ़ंक्शन का नाम फ़ंक्शन के पॉइंटर के बराबर होता है।

आप पुराने कोड, या कोड जो लोग पुराने compilers पर लाया गया द्वारा लिखित में मिल जाएगा, कि कार्यों के लिए संकेत अंकन का उपयोग किया जाता है:

result = (*pointer_to_function)(arg1, arg2, ...); 

आधुनिक शैली में, कि लिखा है:

result = pointer_to_function(arg1, arg2, ...); 

व्यक्तिगत रूप से, मुझे स्पष्ट अव्यवस्था स्पष्ट लगता है, लेकिन सभी सहमत नहीं हैं।

2

के रूप में दूसरों के लिए

(int (*)(void*,void*))(numeric ? numcmp : strcmp) 

उसके बाद निम्न ने बताया है, एक प्रकार डाली

(int (*)(void*,void*)) 

है और अभिव्यक्ति

(numeric ? numcmp : strcmp) 

सी घोषणाओं किया जा सकता है पढ़ने के लिए काफी मुश्किल है, लेकिन सीखना संभव है। विधि आंतरिक भाग में शुरू करने के लिए है और फिर सही एक कदम पर जाएं, फिर एक कदम छोड़ दिया, जारी दाएं, बाएं, दाएं, बाएं, आदि समाप्त होने तक बाहर। अंदरूनी सब कुछ का मूल्यांकन करने से पहले आप एक कोष्ठक से बाहर नहीं जाते हैं। उदाहरण के लिए ऊपर दिए गए प्रकार के लिए, (*) इंगित करता है कि यह एक सूचक है। सूचकांक कोष्ठक के अंदर एकमात्र चीज थी, इसलिए हम इसके बाहर दाईं ओर मूल्यांकन करते हैं। (void*,void*) इंगित करता है कि दो सूचक तर्कों के साथ एक फ़ंक्शन के लिए सूचक है। अंत में int फ़ंक्शन के रिटर्न प्रकार को इंगित करता है। बाहरी कंस्ट्रैसिस इस प्रकार का कास्ट बनाता है। अद्यतन: दो और विस्तृत लेख: The Clockwise/Spiral Rule और Reading C Declarations: A Guide for the Mystified

हालांकि, अच्छी खबर यह है, हालांकि इसके बाद के संस्करण अत्यंत पता करने के लिए उपयोगी है, वहाँ धोखा देने के लिए एक बहुत ही आसान तरीका है:

: cdecl कार्यक्रम सी से अंग्रेजी विवरण और इसके विपरीत करने के लिए परिवर्तित कर सकते हैं
cdecl> explain (int (*)(void*,void*)) 
cast unknown_name into pointer to function (pointer to void, pointer to void) returning int 
cdecl> declare my_var as array 5 of pointer to int 
int *my_var[5] 
cdecl> 

व्यायाम: i किस प्रकार का चर है?

rot13 में
int *(*(*i)[])(int *) 

उत्तर मामले में आप cdecl अपने मशीन पर स्थापित नहीं है (लेकिन क्या तुम सच में जाना चाहिए!):

pqrpy> rkcynva vag *(*(*v)[])(vag *) 
qrpyner v nf cbvagre gb neenl bs cbvagre gb shapgvba (cbvagre gb vag) ergheavat cbvagre gb vag 
pqrpy> 
2

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

ब्रायन Kernighan से कहावत याद रखें:

डिबगिंग दो बार के रूप में कठिन पहली जगह में लेखन कोड के रूप में। इसलिए, यदि आप कोड को जितनी जल्दी हो सके लिखते हैं, तो आप परिभाषा के अनुसार डीबग करने के लिए पर्याप्त स्मार्ट नहीं हैं।


मैं कठिन वास्तविक समय समय सीमा के साथ प्रदर्शन महत्वपूर्ण कोडिंग के बहुत करते हैं ... और मैं अभी भी एक जगह है जहाँ एक घने वन-लाइनर उचित है नहीं देखा है।

मैंने यह भी देखने के लिए एएसएम को संकलित और जांचने के साथ गड़बड़ कर दी है कि एक-लाइनर के पास एक बेहतर संकलित एएसएम कार्यान्वयन है, लेकिन इसे कभी भी एक लाइनर के लायक नहीं मिला है।

+1

मैं यह उल्लेख करना भूल गया कि यह के एंड आर से कोड खंड था, इसलिए शायद केर्निगन ने खुद को विरोधाभास दिया। –

+0

पैर में मुंह डालें ... अब। –

+0

मानक लाइब्रेरी का सही उपयोग करने के लिए यह चतुरता नहीं है ... –

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