2016-04-15 10 views
7

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

जब मैं समारोह संकेत की एक सरणी में पारित, फिर भी, मैं एक चेतावनी है जब मैं -Wpedantic साथ संकलन मिलती है:

बजना:

test.c:40:8: warning: assigning to 'voidfunc' (aka 'void (*)(void)') from 'void *' converts 
     between void pointer and function pointer [-Wpedantic] 

जीसीसी:

test.c:40:8: warning: ISO C forbids assignment between function pointer and ‘void *’ [-Wpedantic] 
    fptr = find_ptr("quux", name_list, (void **)ptr_list, 

यहाँ एक है परीक्षण फ़ाइल, जो चेतावनी के बावजूद सही ढंग से "quux" प्रिंट करती है:

#include <stdio.h> 
#include <string.h> 

void foo(void) 
{ 
    puts("foo"); 
} 

void bar(void) 
{ 
    puts("bar"); 
} 

void quux(void) 
{ 
    puts("quux"); 
} 

typedef void (* voidfunc)(void); 

voidfunc ptr_list[] = {foo, bar, quux}; 

char *name_list[] = {"foo", "bar", "quux"}; 

void *find_ptr(char *name, char *names[], void *ptrs[], int length) 
{ 
    int i; 

    for (i = 0; i < length; i++) { 
    if (strcmp(name, names[i]) == 0) { 
     return ptrs[i]; 
    } 
    } 
    return NULL; 
} 

int main() { 
    voidfunc fptr; 

    fptr = find_ptr("quux", name_list, (void **)ptr_list, 
        sizeof(ptr_list)/sizeof(ptr_list[0])); 
    fptr(); 

    return 0; 
} 

क्या -Wpedantic के साथ संकलित नहीं करने के अलावा चेतावनी को ठीक करने का कोई तरीका है, या मेरे find_ptr फ़ंक्शन को डुप्लिकेट करना, फ़ंक्शन पॉइंटर्स के लिए और एक बार गैर-फ़ंक्शन पॉइंटर्स के लिए? क्या मैं जो करने की कोशिश कर रहा हूं उसे हासिल करने का एक बेहतर तरीका है?

+0

आईआईआरसी मुद्दा यह है कि कोड पॉइंटर्स डेटा पॉइंटर्स के समान आकार के समान नहीं हो सकते हैं। अधिकांश आर्किटेक्चर में वे हैं। सी मानक के संदर्भ को खोजने के लिए मैं इसे किसी और को छोड़ दूंगा। आप शायद 'uintptr_t' और पीछे के माध्यम से कास्टिंग करके इच्छित परिणाम प्राप्त कर सकते हैं। – abligh

+1

@ पॉली द्वारा वर्णित संभावित आकार मिलान के कारण फ़ंक्शन पॉइंटर्स को अन्य पॉइंटर प्रकारों में डालने के लिए यह अनिर्धारित व्यवहार है अधिक जानकारी के लिए यह प्रश्न देखें: http://stackoverflow.com/questions/13696918/c-cast-void-pointer-to -फंक्शन-पॉइंटर – erdeszt

+2

आपको 'void * ptrs []' 'voidfunc * ptrs' द्वारा प्रतिस्थापित करने से क्या रोकता है? – alk

उत्तर

7

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

उदाहरण के लिए ia64 फ़ंक्शन पॉइंटर्स (या कम से कम पिछली बार देखा गया था) वास्तव में दो मान हैं, दोनों साझा पुस्तकालयों या प्रोग्राम और साझा लाइब्रेरी में फ़ंक्शन कॉल करने के लिए आवश्यक हैं। इसी तरह, फ़ंक्शन पॉइंटर्स को फेंकने और कॉल करने के लिए सामान्य अभ्यास, फ़ंक्शन पर एक पॉइंटर को एक फ़ंक्शन पर वापस लौटने के लिए, क्योंकि आप जानते हैं कि आप रिटर्न वैल्यू को अनदेखा करेंगे, फिर भी ia64 पर भी अवैध है क्योंकि इससे रजिस्ट्रारों में लीक करने वाले जाल मूल्यों का कारण बन सकता है कोड के कुछ असंबंधित टुकड़े में बाद में कई निर्देशों में दुर्घटनाएं हुईं।

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

#include <stdio.h> 
#include <string.h> 

void foo(void) 
{ 
    puts("foo"); 
} 

void bar(void) 
{ 
    puts("bar"); 
} 

void quux(void) 
{ 
    puts("quux"); 
} 

typedef void (* voidfunc)(void); 

voidfunc ptr_list[] = {foo, bar, quux}; 

char *name_list[] = {"foo", "bar", "quux"}; 

voidfunc find_ptr(char *name, char *names[], voidfunc ptrs[], int length) 
{ 
    int i; 

    for (i = 0; i < length; i++) { 
     if (strcmp(name, names[i]) == 0) { 
      return ptrs[i]; 
     } 
    } 
    return NULL; 
} 

int main() { 
    voidfunc fptr; 

    fptr = find_ptr("quux", name_list, ptr_list, 
        sizeof(ptr_list)/sizeof(ptr_list[0])); 
    fptr(); 

    return 0; 
} 
+0

ग्रेट उत्तर, लेकिन मेरे पास एक सवाल है: क्या पॉइंटर को 'void (* f)()' के अंदर एक (किसी भी) फ़ंक्शन में स्टोर करना सुरक्षित है और कॉल करते समय इसे उचित प्रकार पर डालना सुरक्षित है? – HolyBlackCat

+2

@ होलीब्लैक कैट जो आज मैं मौजूद वास्तुकला के बारे में जानता हूं, हां। मानक के दृष्टिकोण से, नहीं। भविष्य की संगतता के दृष्टिकोण से, मैं नहीं बल्कि चाहूंगा। इस बात के दृष्टिकोण से कि जब आप टूट जाएंगे तो आप किस कंपनी में होंगे, इसके लिए जाएं। बड़ी परियोजनाओं को देखने के लिए आप क्या देख सकते हैं यह देखने का एक अच्छा तरीका है। उदाहरण के लिए यदि लिनक्स कर्नेल टूट जाता है जब कंपेलरों को किसी चीज के बारे में सख्त हो जाता है तो आप सुनिश्चित हो सकते हैं कि आप इसके बारे में सुनेंगे और कम से कम इसे अक्षम करने के लिए झंडे होंगे। हार्डवेयर आर्किटेक्चर के लिए भी यही है, कुछ आज एक सीपीयू का निर्माण करेंगे जो लिनक्स चालू नहीं हो सकता है। – Art

+1

[यह उत्तर] (http://stackoverflow.com/a/5579907/149138) आपके विरोधाभास करता है। विशेष रूप से, यह मानक उद्धृत करता है जो इंगित करता है कि फ़ंक्शन पॉइंटर्स को किसी भी अन्य फ़ंक्शन पॉइंटर प्रकार में डाला जा सकता है और फिर से वापस किया जा सकता है। – BeeOnRope

5

एक समाधान संकेत का स्तर जोड़ना है। यह बहुत सी चीजों के साथ मदद करता है। फ़ंक्शन में पॉइंटर संग्रहीत करने के बजाय, एक पॉइंटर को struct पर फ़ंक्शन पर पॉइंटर संग्रहीत करने के लिए स्टोर करें।

typedef struct 
{ 
    void (*ptr)(void); 
} Func; 

Func vf = { voidfunc }; 

ptrlist[123] = &vf; 

आदि

+3

आपको 'संरचना' की आवश्यकता नहीं है; आप केवल पॉइंटर-टू-पॉइंटर का उपयोग कर सकते हैं। – davmac

+1

@davmac हां, लेकिन आप संरचना में और सामान जोड़ सकते हैं और एक गरीब व्यक्ति के बंद होने का निर्माण कर सकते हैं। –

1
कुछ संशोधन आप सूचक बातचीत से बच सकते हैं के साथ

डेटा के कार्यों और पॉइंटर्स के लिए पॉइंटर्स।

सी 8 9 मानक से पहले, सभी सी कंपाइलरों को विभिन्न प्रकार के पॉइंटर्स के बीच परिवर्तित करने की इजाजत दी गई, और char * आम तौर पर एक सामान्य सूचक के रूप में उपयोग किया जाता था जो किसी भी डेटा प्रकार या किसी भी फ़ंक्शन को इंगित कर सकता है।C89 ने void * जोड़ा, लेकिन एक खंड में रखा गया है कि केवल ऑब्जेक्ट पॉइंटर्स को void * में परिवर्तित किया जा सकता है, बिना किसी ऑब्जेक्ट को परिभाषित किए। फिर भी, अधिक कोड मौजूद है जो फ़ंक्शन पॉइंटर्स को void * में परिवर्तित करता है और उम्मीद करता है कि यह ठीक से काम करे। नतीजतन, सभी सी कंपाइलर अभी भी इसे अनुमति देते हैं, और अभी भी सही कोड उत्पन्न करते हैं, क्योंकि किसी भी कंपाइलर को अनुपयोगी के रूप में अस्वीकार नहीं किया जाएगा।

सच पूछिये तो, अगर तुम सी में एक सामान्य सूचक करना चाहते हैं, तो आप एक संघ है कि पकड़ सकता है परिभाषित करने की जरूरत है या तो एक void * या एक void (*)() और कॉल करने से पहले सही समारोह सूचक प्रकार के समारोह सूचक की एक स्पष्ट कलाकारों का उपयोग यह।

+0

मैं पॉइंटर सरणी पैरामीटर को फंक्शन पॉइंटर्स के रूप में घोषित नहीं करना चाहता क्योंकि मैं अन्य चीजों के लिए एक ही फ़ंक्शन का उपयोग करता हूं। – sagittarian

5

यह कुछ ऐसा है लंबे सी मानक में टूट गया है और तय नहीं किया गया है है:

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