एक फ़ंक्शन पॉइंटर को दूसरे प्रकार से कास्टिंग करना


88

मान लें कि मेरे पास एक फ़ंक्शन है जो void (*)(void*)कॉलबैक के रूप में उपयोग के लिए फ़ंक्शन पॉइंटर स्वीकार करता है :

void do_stuff(void (*callback_fp)(void*), void* callback_arg);

अब, अगर मेरे पास इस तरह का कोई कार्य है:

void my_callback_function(struct my_struct* arg);

क्या मैं इसे सुरक्षित रूप से कर सकता हूं?

do_stuff((void (*)(void*)) &my_callback_function, NULL);

मैंने इस प्रश्न को देखा है और मैंने कुछ सी मानकों को देखा है जो कहते हैं कि आप 'संगत फ़ंक्शन पॉइंटर्स' के लिए कास्ट कर सकते हैं, लेकिन मुझे इस बात की परिभाषा नहीं मिल सकती है कि 'संगत फ़ंक्शन पॉइंटर' का क्या मतलब है।


1
मैं कुछ हद तक नौसिखिया हूँ लेकिन क्या "शून्य ( ) (शून्य ) फ़ंक्शन पॉइंटर" का मतलब है? क्या यह एक फ़ंक्शन के लिए एक पॉइंटर है जो एक शून्य * को एक तर्क के रूप में स्वीकार करता है और शून्य रिटर्न करता है
डिजिटल गैल

2
@ मायके: void (*func)(void *)इसका मतलब है कि funcएक फ़ंक्शन के लिए एक संकेतक है जैसे कि एक प्रकार का हस्ताक्षर void foo(void *arg)। तो हाँ, आप सही हैं।
mk12

जवाबों:


121

जहां तक ​​सी मानक का संबंध है, यदि आप एक फ़ंक्शन पॉइंटर को एक अलग प्रकार के फ़ंक्शन पॉइंटर में डालते हैं और फिर कॉल करते हैं, तो यह अपरिभाषित व्यवहार है । अनुलग्नक J.2 (सूचनात्मक) देखें:

व्यवहार निम्न परिस्थितियों में अपरिभाषित है:

  • किसी फ़ंक्शन को कॉल करने के लिए एक पॉइंटर का उपयोग किया जाता है जिसका प्रकार इंगित किए गए प्रकार (6.3.2.3) के साथ संगत नहीं है।

धारा 6.3.2.3, पैराग्राफ 8 पढ़ता है:

एक प्रकार के फ़ंक्शन के लिए एक पॉइंटर को दूसरे प्रकार के फ़ंक्शन के लिए पॉइंटर में परिवर्तित किया जा सकता है और फिर से वापस किया जा सकता है; परिणाम मूल सूचक के बराबर होगा। यदि एक परिवर्तित पॉइंटर का उपयोग किसी फ़ंक्शन को कॉल करने के लिए किया जाता है जिसका प्रकार इंगित किए गए प्रकार के साथ संगत नहीं है, तो व्यवहार अपरिभाषित है।

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

संगत की परिभाषा कुछ जटिल है। यह खंड 6.7.5.3, पैराग्राफ 15 में पाया जा सकता है:

दो फ़ंक्शन प्रकार संगत होने के लिए, दोनों संगत रिटर्न प्रकार 127 निर्दिष्ट करेंगे ।

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

127) यदि दोनों प्रकार के प्रकार 'पुरानी शैली' हैं, तो पैरामीटर प्रकार की तुलना नहीं की जाती है।

यह निर्धारित करने के नियम कि क्या दो प्रकार के संगत हैं, खंड 6.2.7 में वर्णित हैं, और मैं उन्हें यहां उद्धृत नहीं करूंगा क्योंकि वे लंबे हैं, लेकिन आप उन्हें C99 मानक (पीडीएफ) के मसौदे पर पढ़ सकते हैं ।

प्रासंगिक नियम यहाँ खंड 6.7.5.1, पैराग्राफ 2 में है:

संगत होने के लिए दो सूचक प्रकारों के लिए, दोनों को पहचान योग्य रूप से योग्य होना चाहिए और दोनों संगत प्रकारों के लिए संकेत होंगे।

इसलिए, चूंकि यह ए void* के साथ संगत नहीं है struct my_struct*, प्रकार void (*)(void*)का एक फ़ंक्शन पॉइंटर प्रकार के फ़ंक्शन पॉइंटर के साथ संगत नहीं है void (*)(struct my_struct*), इसलिए फ़ंक्शन पॉइंटर्स की यह कास्टिंग तकनीकी रूप से अपरिभाषित व्यवहार है।

व्यवहार में, हालांकि, आप कुछ मामलों में कास्टिंग फ़ंक्शन के साथ सुरक्षित रूप से दूर हो सकते हैं। X86 कॉलिंग कन्वेंशन में, स्टैक पर तर्क दिए गए हैं, और सभी पॉइंटर्स समान आकार (x86 में 4 बाइट्स या x86_64 में 8 बाइट्स) हैं। एक फ़ंक्शन पॉइंटर को कॉल करना स्टैक पर तर्कों को आगे बढ़ाने के लिए उबलता है और फ़ंक्शन पॉइंटर लक्ष्य के लिए एक अप्रत्यक्ष कूद करता है, और मशीन कोड स्तर पर स्पष्ट रूप से प्रकार की कोई धारणा नहीं है।

चीजें जो आप निश्चित रूप से नहीं कर सकते कर हैं:

  • विभिन्न कॉलिंग सम्मेलनों के फ़ंक्शन पॉइंटर्स के बीच कास्ट करें। आप स्टैक को गड़बड़ कर देंगे और सबसे अच्छा, क्रैश, सबसे कम, एक विशाल गैपिंग सुरक्षा छेद के साथ चुपचाप सफल होंगे। विंडोज प्रोग्रामिंग में, आप अक्सर फ़ंक्शन पॉइंटर्स को पास करते हैं। Win32 stdcallकॉलिंग कन्वेंशन (जो मैक्रोज़ , और CALLBACK, का उपयोग करने के लिए सभी कॉलबैक फ़ंक्शन की अपेक्षा करता हैPASCALWINAPI सभी का विस्तार)। यदि आप एक फ़ंक्शन पॉइंटर पास करते हैं जो मानक सी कॉलिंग कन्वेंशन ( cdecl) का उपयोग करता है , तो परिणाम खराब होगा।
  • C ++ में, क्लास मेंबर फंक्शन पॉइंट्स और रेगुलर फंक्शन पॉइंटर्स के बीच कास्ट किया जाता है। यह अक्सर C ++ न्यूबॉब तक यात्रा करता है। कक्षा के सदस्यों के कार्यों में एक छिपा हुआ thisपैरामीटर होता है, और यदि आप एक सदस्य फ़ंक्शन को नियमित फ़ंक्शन में रखते हैं, तो thisउपयोग करने के लिए कोई वस्तु नहीं है , और फिर से, बहुत खराब परिणाम होगा।

एक और बुरा विचार जो कभी-कभी काम करता है लेकिन अपरिभाषित व्यवहार भी होता है:

  • फंक्शन पॉइंटर्स और रेगुलर पॉइंटर्स (जैसे कास्टिंग void (*)(void)void*) के बीच कास्टिंग । फंक्शन पॉइंटर्स नियमित रूप से पॉइंटर्स के समान आकार के नहीं होते हैं, क्योंकि कुछ आर्किटेक्चर पर उनके पास अतिरिक्त प्रासंगिक जानकारी हो सकती है। यह शायद x86 पर ठीक काम करेगा, लेकिन याद रखें कि यह अपरिभाषित व्यवहार है।

18
क्या यह पूरी बात नहीं void*है कि वे किसी अन्य सूचक के साथ संगत हैं? ए struct my_struct*को कास्टिंग करने में कोई समस्या नहीं होनी चाहिए void*, वास्तव में आपको भी नहीं डालना चाहिए, कंपाइलर को इसे स्वीकार करना चाहिए। उदाहरण के लिए, यदि आप किसी struct my_struct*ऐसे फ़ंक्शन को पास करते हैं जिसमें void*कोई कास्टिंग की आवश्यकता नहीं है। मुझे यहाँ क्या याद आ रहा है जो इन असंगत बनाता है?
ब्रायनमर्न्स

2
यह उत्तर "यह शायद x86 पर ठीक काम करेगा ..." का संदर्भ देता है: क्या ऐसे कोई मंच हैं जहां यह काम नहीं करेगा? किसी को भी अनुभव होता है जब यह विफल हो गया? C के लिए qsort () यदि संभव हो तो फ़ंक्शन पॉइंटर डालने के लिए एक अच्छी जगह की तरह लगता है।
केविनरपे 17

4
@KCArpe: इस आलेख में "सदस्य फ़ंक्शन पॉइंटर्स के कार्यान्वयन" शीर्षक के तहत चार्ट के अनुसार , 16-बिट ओपनवाटकॉम कंपाइलर कभी-कभी कुछ कॉन्फ़िगरेशन में डेटा पॉइंटर प्रकार (2 बाइट्स) की तुलना में बड़े फ़ंक्शन पॉइंटर प्रकार (4 बाइट्स) का उपयोग करता है। हालाँकि, POSIX-conforming सिस्टम को void*फ़ंक्शन पॉइंटर प्रकारों के लिए समान प्रतिनिधित्व का उपयोग करना होगा , कल्पना देखें ।
एडम रोसेनफील्ड

3
@Adam का लिंक अब POSIX मानक के 2016 संस्करण को संदर्भित करता है जहां संबंधित खंड 2.12.3 हटा दिया गया है। आप इसे अभी भी 2008 संस्करण में पा सकते हैं ।
मार्टिन ट्रेनेकमैन

6
@ ब्रायनमर्न्स नहीं, void *केवल "किसी भी अन्य (गैर-फ़ंक्शन) पॉइंटर के साथ बहुत सटीक रूप से परिभाषित तरीकों से संगत है (जो कि इस मामले में" संगत "शब्द के साथ सी मानक का क्या अर्थ है, इसके लिए असंबंधित हैं)। C एक void *बड़ा या छोटे होने की अनुमति देता है struct my_struct *, या बिट्स को अलग-अलग क्रम में या नकारात्मक या जो भी हो। तो void f(void *)और एबीआई-असंगतvoid f(struct my_struct *) हो सकता है । यदि आवश्यक हुआ तो C आपके लिए पॉइंटर्स को स्वयं में परिवर्तित कर देगा, लेकिन यह कभी-कभी भिन्न तर्क प्रकार लेने के लिए एक पॉइंट-इन फ़ंक्शन को परिवर्तित नहीं कर सकता है।
mtraceur

32

मैंने हाल ही में GLib में कुछ कोड के बारे में इसी सटीक मुद्दे के बारे में पूछा। (GLib GNOME प्रोजेक्ट के लिए एक कोर लाइब्रेरी है और C. में लिखा गया है।) मुझे बताया गया था कि यह पूरी तरह से स्लॉट्स के एग्निल्स फ्रेमवर्क पर निर्भर है।

कोड भर में, टाइपिंग (1) से (2) तक कास्टिंग के कई उदाहरण हैं:

  1. typedef int (*CompareFunc) (const void *a, const void *b)
  2. typedef int (*CompareDataFunc) (const void *b, const void *b, void *user_data)

इस तरह से कॉल के साथ चेन-थ्रू करना आम है:

int stuff_equal (GStuff      *a,
                 GStuff      *b,
                 CompareFunc  compare_func)
{
    return stuff_equal_with_data(a, b, (CompareDataFunc) compare_func, NULL);
}

int stuff_equal_with_data (GStuff          *a,
                           GStuff          *b,
                           CompareDataFunc  compare_func,
                           void            *user_data)
{
    int result;
    /* do some work here */
    result = compare_func (data1, data2, user_data);
    return result;
}

अपने लिए यहां देखें g_array_sort(): http://git.gnome.org/browse/glib/tree/glib/garray.c

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

मैंने इन उत्तरों के बारे में सोचने में कुछ समय बिताया। यहाँ मेरा निष्कर्ष है:

  1. यदि आप कॉलबैक लाइब्रेरी लिख रहे हैं, तो यह ठीक हो सकता है। कैविट एमप्टर - अपने जोखिम पर उपयोग करें।
  2. और, यह मत करो।

इस प्रतिक्रिया को लिखने के बाद गहराई से सोचने पर, मुझे आश्चर्य नहीं होगा कि सी कंपाइलर के लिए कोड इसी चाल का उपयोग करता है। और चूंकि (अधिकांश / सभी?) आधुनिक सी कंपाइलरों को बूटस्ट्रैप किया जाता है, यह होगा कि चाल सुरक्षित है।

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


10
एक जगह जहाँ यह निश्चित रूप से काम नहीं करता है वह जावास्क्रिप्ट एलवीएम टू जावास्क्रिप्ट कम्पाइलर है। देखें github.com/kripken/emscripten/wiki/Asm-pointer-casts जानकारी के लिए।
बेन लिंग्स

2
के बारे में Upated संदर्भ Emscripten
ysdx

4
लिंक @BenLings निकट भविष्य में टूट जाएगा। यह आधिकारिक तौर पर kripken.github.io/emscripten-site/docs/porting/guidelines/…
एलेक्स रिंकिंग

9

मुद्दा यह नहीं है कि क्या आप कर सकते हैं। तुच्छ समाधान है

void my_callback_function(struct my_struct* arg);
void my_callback_helper(void* pv)
{
    my_callback_function((struct my_struct*)pv);
}
do_stuff(&my_callback_helper);

एक अच्छा संकलक केवल my_callback_helper के लिए कोड उत्पन्न करेगा यदि यह वास्तव में आवश्यक है, तो उस स्थिति में जिसे आपको खुशी होगी।


समस्या यह नहीं है एक सामान्य समाधान है। इसे फ़ंक्शन के ज्ञान के साथ केस के आधार पर किया जाना चाहिए। यदि आपके पास पहले से ही गलत प्रकार का फ़ंक्शन है, तो आप अटक गए हैं।
BeeOnRope

सभी संकलक जिनके साथ मैंने इसका परीक्षण किया my_callback_helper, उनके लिए कोड उत्पन्न होगा , जब तक कि यह हमेशा इनलेट न हो। यह निश्चित रूप से आवश्यक नहीं है, क्योंकि केवल एक चीज जो इसे करना है jmp my_callback_function। संकलक शायद यह सुनिश्चित करना चाहता है कि फ़ंक्शन के पते अलग-अलग हैं, लेकिन दुर्भाग्य से यह तब भी होता है जब फ़ंक्शन को C99 के साथ चिह्नित किया जाता है inline(यानी "पते की परवाह न करें")।
yyny

मुझे यकीन नहीं है कि यह सही है। ऊपर एक अन्य उत्तर से (@mtraceur द्वारा) एक अन्य टिप्पणी में कहा गया है कि एक void *से भी अलग आकार का हो सकता है struct *(मुझे लगता है कि यह गलत है, क्योंकि अन्यथा mallocटूट जाएगा, लेकिन उस टिप्पणी में 5 अपवोट हैं, इसलिए मैं इसे कुछ क्रेडिट दे रहा हूं। यदि @mtraceur सही है, तो आपने जो समाधान लिखा है वह सही नहीं होगा।
सेस

@ अधिक: अगर आकार अलग है तो यह बिल्कुल भी मायने नहीं रखता। void*अभी भी और इससे होने वाले रूपांतरण को काम करना है। संक्षेप में, void*अधिक बिट्स हो सकता है, लेकिन यदि आप एक डाली struct*को void*उन अतिरिक्त बिट्स शून्य हो सकता है और कलाकारों वापस बस फिर उन शून्यों त्यागने कर सकते हैं।
MSALERS

@MSalters: मैं वास्तव में एक void *(सिद्धांत में) एक से अलग हो सकता है पता नहीं था struct *। मैं सी में एक विएबेट को लागू कर रहा हूं, और मैं thisवर्चुअल फ़ंक्शन के पहले तर्क के रूप में सी ++ - ईश पॉइंटर का उपयोग कर रहा हूं । जाहिर है, this"वर्तमान" (व्युत्पन्न) संरचना के लिए एक संकेतक होना चाहिए। इसलिए, वर्चुअल फ़ंक्शन को उनके द्वारा लागू किए गए void *this
ढांचे के

6

आपके पास एक संगत फ़ंक्शन प्रकार है यदि रिटर्न प्रकार और पैरामीटर प्रकार संगत हैं - मूल रूप से (यह वास्तविकता में अधिक जटिल है :))। संगतता "एक ही प्रकार" के समान है और विभिन्न प्रकारों के लिए अनुमति देने के लिए सिर्फ और अधिक शिथिलता है, लेकिन फिर भी यह कहना है कि "ये प्रकार लगभग समान हैं"। C89 में, उदाहरण के लिए, दो संरचनाएं संगत थीं यदि वे अन्यथा समान थीं लेकिन सिर्फ उनका नाम अलग था। C99 लगता है कि बदल गया है। ग औचित्य दस्तावेज़ से उद्धृत (अत्यधिक अनुशंसित पढ़ने, btw!):

दो अलग-अलग अनुवाद इकाइयों में संरचना, संघ, या गणना प्रकार की घोषणाएं औपचारिक रूप से एक ही प्रकार की घोषणा नहीं करती हैं, भले ही इन घोषणाओं के पाठ में एक ही फाइल शामिल हो, क्योंकि अनुवाद इकाइयां स्वयं असंतुष्ट हैं। मानक इस प्रकार के प्रकारों के लिए अतिरिक्त संगतता नियम निर्दिष्ट करता है, ताकि यदि दो ऐसी घोषणाएं समान रूप से समान हों तो वे संगत हों।

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


4

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

मुझे उम्मीद है कि मैं यह दिखा कर स्पष्ट कर सकता हूं कि क्या काम नहीं करेगा:

int my_number = 14;
do_stuff((void (*)(void*)) &my_callback_function, &my_number);
// my_callback_function will try to access int as struct my_struct
// and go nuts

या ...

void another_callback_function(struct my_struct* arg, int arg2) { something }
do_stuff((void (*)(void*)) &another_callback_function, NULL);
// another_callback_function will look for non-existing second argument
// on the stack and go nuts

मूल रूप से, जब तक आप डेटा को रन-टाइम पर समझाना जारी रखते हैं, तब तक आप अपनी पसंद के अनुसार पॉइंटर्स डाल सकते हैं।


0

यदि आप C / C ++ में फ़ंक्शन कॉल के काम के तरीके के बारे में सोचते हैं, तो वे स्टैक पर कुछ वस्तुओं को धक्का देते हैं, नए कोड स्थान पर कूदते हैं, निष्पादित करते हैं, फिर रिटर्न पर स्टैक को पॉप करते हैं। यदि आपके फ़ंक्शन पॉइंटर्स समान रिटर्न प्रकार और तर्कों के समान संख्या / आकार के साथ फ़ंक्शन का वर्णन करते हैं, तो आपको ठीक होना चाहिए।

इस प्रकार, मुझे लगता है कि आपको ऐसा करने में सक्षम होना चाहिए।


2
आप केवल तभी तक सुरक्षित हैं जब तक- structपॉइंटर्स और void-पॉइंटर्स में संगत बिट-अभ्यावेदन होते हैं; यह मामला होने की गारंटी नहीं है
क्रिस्टोफ

1
कंपाइलर रजिस्टरों में भी तर्क पारित कर सकते हैं। और फ़्लोट्स, इनट्स या पॉइंटर्स के लिए विभिन्न रजिस्टरों का उपयोग करना अनसुना नहीं है।
MSALERS

0

शून्य सूचक अन्य प्रकार के सूचक के साथ संगत हैं। यह कैसे मालोक और मेम कार्यों ( memcpy, memcmp) काम की रीढ़ है । आमतौर पर, C में (C ++ की बजाय) NULLएक मैक्रो है, जिसे परिभाषित किया गया है ((void *)0)

C99 में 6.3.2.3 (आइटम 1) देखें:

शून्य करने के लिए एक सूचक को सूचक से किसी भी अपूर्ण या वस्तु प्रकार में परिवर्तित किया जा सकता है


एडम रोसेनफील्ड के जवाब में यह विरोधाभास है , अंतिम पैराग्राफ और टिप्पणियां देखें
उपयोगकर्ता

1
यह उत्तर स्पष्ट रूप से गलत है। फ़ंक्शन पॉइंटर को छोड़कर , कोई भी पॉइंटर एक शून्य पॉइंटर के लिए परिवर्तनीय है ।
मार्टन78
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.