फ़ंक्शन पॉइंटर्स और डेटा पॉइंटर्स C / C ++ में असंगत क्यों हैं?


130

मैंने पढ़ा है कि एक फ़ंक्शन पॉइंटर को डेटा पॉइंटर में कनवर्ट करना और इसके विपरीत अधिकांश प्लेटफार्मों पर काम करता है, लेकिन काम करने की गारंटी नहीं है। यह एक केस क्यों है? क्या दोनों को मुख्य स्मृति में केवल पते नहीं होना चाहिए और इसलिए संगत होना चाहिए?


16
POSIX में परिभाषित मानक C में अपरिभाषित है। अंतर मन।

मैं इस पर थोड़ा नया हूँ, लेकिन क्या आप "=" के दाईं ओर कास्ट करने वाले नहीं हैं? मुझे लगता है कि समस्या यह है कि आप एक शून्य सूचक को असाइन कर रहे हैं। लेकिन मैं देख रहा हूं कि मैन पेज ऐसा करता है, इसलिए उम्मीद है कि कोई मुझे शिक्षित कर सकता है। मैं उदाहरण के लिए 'dlsym से रिटर्न वैल्यू की ढलाई कर रहे लोगों के जाल, उदाहरण के लिए यहाँ देखता हूँ
JasonWoof

9
नोट करें कि POSIX डेटा प्रकारों पर अनुभाग में क्या कहता है : .32.12.3 सूचक प्रकार। सभी फ़ंक्शन पॉइंटर प्रकारों के लिए पॉइंटर पॉइंटर के समान प्रतिनिधित्व होगा void। फ़ंक्शन पॉइंटर का रूपांतरण void *प्रतिनिधित्व में परिवर्तन नहीं करेगा। इस void *तरह के रूपांतरण से उत्पन्न मूल्य को मूल फ़ंक्शन पॉइंटर प्रकार में परिवर्तित किया जा सकता है, एक स्पष्ट कलाकारों का उपयोग करके, बिना जानकारी के नुकसान के। नोट : ISO C मानक को इसकी आवश्यकता नहीं है, लेकिन इसके लिए POSIX अनुरूपता आवश्यक है।
जोनाथन लेफ़लर

2
इस वेबसाइट के ABOUT सेक्शन में यह प्रश्न है
ज़ू ज़ेन

1
@KeithThompson: दुनिया बदलती है - और POSIX भी। मैंने 2012 में जो लिखा था वह अब 2018 में लागू नहीं होगा। पोसिक्स मानक ने क्रिया को बदल दिया। यह अब से जुड़ा हुआ है dlsym()- 'एप्लिकेशन उपयोग' अनुभाग के अंत पर ध्यान दें जहां यह कहता है: ध्यान दें कि एक void *पॉइंटर से एक फ़ंक्शन पॉइंटर में रूपांतरण इस रूप में है: fptr = (int (*)(int))dlsym(handle, "my_function"); आईएसओ सी मानक द्वारा परिभाषित नहीं है। इस मानक के अनुरूप कार्यान्वयन पर सही ढंग से काम करने के लिए इस रूपांतरण की आवश्यकता होती है।
जोनाथन लेफ्लर 20

जवाबों:


171

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


15
इसके अलावा, भले ही कोड और डेटा भौतिक हार्डवेयर में एक ही स्थान पर संग्रहीत हों, सॉफ़्टवेयर और मेमोरी एक्सेस अक्सर ऑपरेटिंग सिस्टम "अनुमोदन" के बिना कोड के रूप में डेटा को चलाने से रोकते हैं। DEP और पसंद है।
माइकल ग्रेसीक

15
कम से कम महत्वपूर्ण के रूप में अलग पते रिक्त स्थान (शायद अधिक महत्वपूर्ण) होने के रूप में महत्वपूर्ण है कि समारोह संकेत डेटा बिंदुओं की तुलना में एक अलग प्रतिनिधित्व हो सकता है।
माइकल बूर

14
आपको अलग-अलग एड्रेस स्पेस का उपयोग करके कोड और डेटा पॉइंटर्स रखने के लिए हार्वर्ड आर्किटेक्चर होना भी जरूरी नहीं है - पुराने डॉस "स्मॉल" मेमोरी मॉडल ने ऐसा किया (पास पॉइंटर्स के साथ CS != DS)।
कैफे

1
यहां तक ​​कि आधुनिक प्रोसेसर भी इस तरह के मिश्रण के साथ संघर्ष करेंगे क्योंकि निर्देश और डेटा कैश आमतौर पर अलग से संभाला जाता है, तब भी जब ऑपरेटिंग सिस्टम आपको कहीं कोड लिखने की अनुमति देता है।
PypeBros

3
@EricJ। जब तक आप कॉल नहीं करते हैं VirtualProtect, जो आपको निष्पादन योग्य के रूप में डेटा के क्षेत्रों को चिह्नित करने की अनुमति देता है।
डिट्रीच एप ईपी

37

कुछ कंप्यूटरों में कोड और डेटा के लिए अलग-अलग पता स्थान होते हैं। ऐसे हार्डवेयर पर यह काम नहीं करता है।

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


ऐसा लगता है कि सी भाषा समिति ने कभी भी void*कार्य करने के लिए एक संकेतक होने का इरादा नहीं किया था , वे सिर्फ वस्तुओं के लिए एक सामान्य सूचक चाहते थे।

C99 राशनले कहते हैं:

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

जेनेरिक ऑब्जेक्ट पॉइंटर प्रकार के रूप में void*("पॉइंटर टू void") का उपयोग C89 समिति का एक आविष्कार है। इस प्रकार के अडॉप्शन को फ़ंक्शन प्रोटोटाइप तर्कों को निर्दिष्ट करने की इच्छा से प्रेरित किया गया था जो या तो चुपचाप मनमाना पॉइंटर्स (जैसे fread) में परिवर्तित करते हैं या शिकायत करते हैं कि तर्क प्रकार बिल्कुल मेल नहीं खाता (जैसा कि strcmp)। कार्यों के लिए पॉइंटर्स के बारे में कुछ भी नहीं कहा जाता है, जो ऑब्जेक्ट पॉइंटर्स और / या पूर्णांक के साथ इनकम हो सकता है।

नोट कुछ भी नहीं कहा जाता है कि कार्यों के लिए संकेत अंतिम पैराग्राफ में गया है। वे अन्य बिंदुओं से भिन्न हो सकते हैं, और समिति इससे अवगत है।


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

15
@CrazyEddie आप किसी फ़ंक्शन पॉइंटर को a को असाइन नहीं कर सकते void *

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

5
लेकिन ऐसे sizeof(void*) == sizeof( void(*)() )मामले में जगह की आवश्यकता होती है जहां फ़ंक्शन पॉइंटर्स और डेटा पॉइंटर्स अलग-अलग आकार के होते हैं। 80 के दशक में यह एक सामान्य मामला था, जब पहला सी मानक लिखा गया था।
Rob

8
@ रीचर्डचैम्बर्स: अलग-अलग एड्रेस स्पेस में अलग-अलग एड्रेस की चौड़ाई भी हो सकती है , जैसे कि Atmel AVR जो निर्देशों के लिए 16 बिट्स और डेटा के लिए 8 बिट्स का उपयोग करता है; उस स्थिति में, यह डेटा (8 बिट) से फ़ंक्शन (16 बिट) इंगित करने और फिर से वापस बदलने में कठिन होगा। सी को लागू करने में आसान माना जाता है; उस आसानी का हिस्सा डेटा और इंस्ट्रक्शन पॉइंटर्स को एक दूसरे के साथ असंगत छोड़ने से आता है।
जॉन बॉड

30

एमएस-डॉस, विंडोज 3.1 और पुराने उत्तर को याद करने वालों के लिए यह काफी आसान है। ये सभी कोड और डेटा पॉइंटर्स के लिए अलग-अलग संयोजनों के साथ कई अलग-अलग मेमोरी मॉडल का समर्थन करते थे।

उदाहरण के लिए कॉम्पैक्ट मॉडल (छोटे कोड, बड़े डेटा) के लिए:

sizeof(void *) > sizeof(void(*)())

और मध्यम मॉडल (बड़े कोड, छोटे डेटा) में इसके विपरीत:

sizeof(void *) < sizeof(void(*)())

इस स्थिति में आपके पास कोड और दिनांक के लिए अलग-अलग संग्रहण नहीं था, लेकिन फिर भी दोनों बिंदुओं (गैर-मानक __near और __far संशोधक का उपयोग करने में कमी) के बीच परिवर्तित नहीं हो सका।

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


पुन: "एक फ़ंक्शन पॉइंटर को एक डेटा पॉइंटर में कनवर्ट करने से आपको एक पॉइंटर नहीं मिलेगा, जिसका फ़ंक्शन में बिल्कुल भी कोई संबंध नहीं था, और इसलिए इस तरह के रूपांतरण के लिए कोई उपयोग नहीं था": यह पूरी तरह से पालन नहीं करता है। एक परिवर्तित int*एक करने के लिए void*दे आप एक सूचक है कि तुम सच के साथ कुछ नहीं कर सकते, लेकिन यह अभी भी उपयोगी है रूपांतरण करने के लिए सक्षम होने के लिए। (यह इसलिए है क्योंकि किसी भी ऑब्जेक्ट पॉइंटर void*को स्टोर कर सकते हैं , इसलिए जेनेरिक एल्गोरिदम के लिए इस्तेमाल किया जा सकता है, जिसे यह जानने की जरूरत नहीं है कि वे किस प्रकार का होल्ड करते हैं। यही बात फंक्शन पॉइंटर्स के लिए भी उपयोगी हो सकती है, अगर इसकी अनुमति दी गई हो।)
ruakh

4
@ruakh: परिवर्तित करने के मामले में int *करने के लिए void *, void *कम से कम बात करने के लिए एक ही वस्तु के रूप में मूल की गारंटी है int *था - तो यह सामान्य एल्गोरिदम के लिए उपयोगी है कि पहुँच उठाई-टू वस्तु, जैसे int n; memcpy(&n, src, sizeof n);। ऐसे मामले में जहां फ़ंक्शन पॉइंटर को परिवर्तित करने से फ़ंक्शन void *पर इंगित करने वाले पॉइंटर का उत्पादन नहीं होता है, यह ऐसे एल्गोरिदम के लिए उपयोगी नहीं है - केवल एक चीज जो आप कर सकते हैं, वह void *वापस फ़ंक्शन पॉइंटर में बदल सकती है , इसलिए आप इस प्रकार हो सकते हैं अच्छी तरह से सिर्फ unionएक void *और एक समारोह सूचक युक्त का उपयोग करें ।
कैफे

@ कफ: काफी साफ है। यह बात बताने के लिए धन्यवाद। और उस बात के लिए, भले ही void* किया कार्य करने के लिए बिंदु, मैं इसे लोगों के लिए इसे पारित करने के लिए एक बुरा विचार किया जाएगा लगता है memcpy। :-P
12

ऊपर से प्रतिलिपि बनाई गई : नोट करें कि POSIX डेटा प्रकारों में क्या कहता है : P2.12.3 सूचक प्रकार। सभी फ़ंक्शन पॉइंटर प्रकारों के लिए पॉइंटर पॉइंटर के समान प्रतिनिधित्व होगा void। फ़ंक्शन पॉइंटर का रूपांतरण void *प्रतिनिधित्व में परिवर्तन नहीं करेगा। इस void *तरह के रूपांतरण से उत्पन्न एक मूल्य को मूल फ़ंक्शन पॉइंटर प्रकार में परिवर्तित किया जा सकता है, एक स्पष्ट कलाकारों का उपयोग करके, बिना जानकारी के नुकसान के। नोट : ISO C मानक को इसकी आवश्यकता नहीं है, लेकिन इसके लिए POSIX अनुरूपता आवश्यक है।
जोनाथन लेफलर

@caf यदि इसे केवल कुछ कॉलबैक से गुजरना चाहिए जो उचित प्रकार जानता है, तो मैं केवल राउंड-ट्रिप सुरक्षा में दिलचस्पी रखता हूं, न कि कोई अन्य संबंध जो कि परिवर्तित मान हो सकते हैं।
Deduplicator

23

शून्य को इंगित करने वाले को किसी भी प्रकार के डेटा के लिए एक पॉइंटर को समायोजित करने में सक्षम होना चाहिए - लेकिन जरूरी नहीं कि एक फ़ंक्शन के लिए एक पॉइंटर। कुछ सिस्टम में पॉइंटर्स से डेटा तक पॉइंटर्स के लिए अलग-अलग आवश्यकताएं होती हैं (जैसे, डेटा बनाम कोड के लिए अलग-अलग एड्रेसिंग के साथ डीएसपी हैं, एमएस-डॉस पर मध्यम मॉडल कोड के लिए 32-बिट पॉइंटर्स का उपयोग करते हैं लेकिन डेटा के लिए केवल 16-बिट पॉइंटर्स हैं) ।


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

1
@Knickerkicker: हाँ, शायद। यदि स्मृति कार्य करती है, तो Dlsym से वापसी प्रकार पर चर्चा की गई थी, शायद 9 या 10 साल पहले, OpenGroup की ईमेल सूची पर। ऑफहैंड, मुझे याद नहीं है कि क्या (अगर कुछ भी) इसके बारे में आया था।
जेरी कॉफ़िन

1
आप सही हे। यह आपकी बात का एक अच्छा (हालांकि पुराना) सारांश है।
मानव


2
@LegoStormtroopr: दिलचस्प है कि 21 लोग कैसे मतदान के विचार से सहमत हैं , लेकिन केवल 3 के बारे में वास्तव में ऐसा हुआ है। :-)
जेरी कॉफ़िन

13

यहाँ पहले से ही क्या कहा गया है, इसके अलावा POSIX को देखना दिलचस्प है dlsym():

ISO C मानक के लिए यह आवश्यक नहीं है कि कार्य करने के लिए संकेत बिंदुओं को डेटा के लिए आगे और पीछे डाला जा सकता है। दरअसल, आईएसओ सी मानक के लिए यह आवश्यक नहीं है कि प्रकार की कोई वस्तु * किसी फ़ंक्शन को पॉइंटर पकड़ सके। XSI एक्सटेंशन का समर्थन करने वाले कार्यान्वयन, हालांकि, यह आवश्यक है कि प्रकार की एक वस्तु * एक फ़ंक्शन के लिए एक पॉइंटर पकड़ सकती है। एक पॉइंटर को एक पॉइंटर में दूसरे डेटा टाइप (शून्य * को छोड़कर) में पॉइंटर में बदलने का नतीजा अभी तक अपरिभाषित है। ध्यान दें कि आईएसओ सी मानक के अनुरूप कंपाइलर को एक चेतावनी उत्पन्न करने के लिए आवश्यक है यदि एक फ़ंक्शन पॉइंटर से पॉइंटर * पॉइंटर से रूपांतरण इस प्रकार किया जाता है:

 fptr = (int (*)(int))dlsym(handle, "my_function");

यहां बताई गई समस्या के कारण, एक भविष्य का संस्करण या तो फ़ंक्शन पॉइंटर्स को वापस करने के लिए एक नया फ़ंक्शन जोड़ सकता है, या वर्तमान इंटरफ़ेस को दो नए फ़ंक्शन के पक्ष में पदावनत किया जा सकता है: एक जो डेटा पॉइंटर्स लौटाता है और दूसरा जो फ़ंक्शन पॉइंटर्स लौटाता है।


इसका मतलब यह है कि किसी फ़ंक्शन का पता पाने के लिए dlsym का उपयोग करना वर्तमान में असुरक्षित है? क्या वर्तमान में इसे करने का कोई सुरक्षित तरीका है?
20

4
इसका मतलब यह है कि वर्तमान में POSIX को एक मंच ABI की आवश्यकता है जो फ़ंक्शन और डेटा पॉइंटर्स दोनों को सुरक्षित रूप से void*वापस और पीछे डाला जा सकता है ।
मैक्सिम इगोरुस्किन

@gexicide इसका मतलब है कि कार्यान्वयन जो कि POSIX अनुरूप हैं, ने भाषा के लिए एक विस्तार किया है, जो कि मानक जड़ता के अनुसार अपरिभाषित व्यवहार के लिए एक कार्यान्वयन-परिभाषित अर्थ देता है। इसे C99 मानक, खंड J.5.7 फ़ंक्शन पॉइंटर कास्ट्स के सामान्य एक्सटेंशन में से एक के रूप में भी सूचीबद्ध किया गया है।
डेविड हैमेन

1
@David Hammen यह भाषा का विस्तार नहीं है, बल्कि एक नई अतिरिक्त आवश्यकता है। C void*को एक पॉइंटर पॉइंटर के साथ संगत होने की आवश्यकता नहीं है , जबकि POSIX करता है।
मैक्सिम एगोरुस्किन

9

C ++ 11 के संबंध में C / C ++ और POSIX के बीच लंबे समय से चली आ रही बेमेल का समाधान है dlsym()। एक reinterpret_castफ़ंक्शन पॉइंटर को / से डेटा पॉइंटर से इतनी देर तक बदलने के लिए उपयोग कर सकता है जब तक कि कार्यान्वयन इस सुविधा का समर्थन करता है।

मानक से, 5.2.10 पैरा। 8, "एक फ़ंक्शन पॉइंटर को ऑब्जेक्ट पॉइंटर प्रकार में परिवर्तित करना या इसके विपरीत सशर्त रूप से समर्थित है।" 1.3.5 "सशर्त रूप से समर्थित" को "प्रोग्राम निर्माण के रूप में परिभाषित करता है कि एक कार्यान्वयन का समर्थन करने की आवश्यकता नहीं है"।


एक कर सकते हैं, लेकिन एक नहीं करना चाहिए। एक अनुरूप संकलक के लिए एक चेतावनी उत्पन्न करनी चाहिए (जो बदले में एक त्रुटि को ट्रिगर करना चाहिए, cf. -Werror)। एक बेहतर (और गैर-यूबी) समाधान एक पॉइंटर को पुनः प्राप्त करने के लिए है dlsym(यानी void**) द्वारा लौटाया जाता है और इसे पॉइंटर से फ़ंक्शन पॉइंटर में परिवर्तित किया जाता हैअभी भी कार्यान्वयन-परिभाषित लेकिन चेतावनी / त्रुटि का कोई कारण नहीं है
कोनराड रुडोल्फ

3
@KonradRudolph: असहमत। "सशर्त रूप से समर्थित" शब्दांकन विशेष रूप से अनुमति देने dlsymऔर GetProcAddressचेतावनी के बिना संकलन करने के लिए लिखा गया था ।
MSALERS

@ दलाल क्या मतलब है, "असहमत"? या तो मैं सही हूं या गलत। Dlsym प्रलेखन स्पष्ट रूप से कहते हैं कि "आईएसओ सी मानक के अनुरूप compilers अगर एक समारोह सूचक को एक शून्य * सूचक से एक रूपांतरण का प्रयास किया है एक चेतावनी उत्पन्न करने के लिए आवश्यक हैं।" यह अटकलबाजी के लिए बहुत जगह नहीं छोड़ता है। और (के साथ जीसीसी -pedantic) है चेतावनी दी है। फिर, कोई अटकलें संभव नहीं।
कोनराड रुडोल्फ

1
अनुगमन: मुझे लगता है कि अब मैं समझता हूं। यह यूबी नहीं है। यह कार्यान्वयन-परिभाषित है। मैं अभी भी अनिश्चित हूं कि चेतावनी उत्पन्न होनी चाहिए या नहीं - शायद नहीं। ओह अच्छा।
कोनराड रुडोल्फ

2
@KonradRudolph: मैं आपकी "इच्छा" से असहमत था, जो एक राय है। उत्तर में C ++ 11 का विशेष रूप से उल्लेख किया गया था, और जिस समय समस्या का समाधान किया गया था, उस समय मैं C ++ CWG का सदस्य था। C99 में वास्तव में अलग-अलग शब्द हैं, सशर्त रूप से समर्थित C ++ आविष्कार है।
8

7

लक्ष्य वास्तुकला के आधार पर, कोड और डेटा को मौलिक रूप से असंगत, शारीरिक रूप से स्मृति के अलग-अलग क्षेत्रों में संग्रहीत किया जा सकता है।


'शारीरिक रूप से विशिष्ट' मैं समझता हूं, लेकिन क्या आप 'मौलिक रूप से असंगत' भेद पर अधिक विस्तार कर सकते हैं। जैसा कि मैंने सवाल में कहा है, किसी भी पॉइंटर प्रकार के रूप में एक बड़ा पॉइंटर माना नहीं जाता है - या यह कि मेरी ओर से गलत अनुमान है।
मानव रचना

@KnickerKicker: void *किसी भी डेटा पॉइंटर को होल्ड करने के लिए काफी बड़ा है, लेकिन जरूरी नहीं कि कोई फंक्शन पॉइंटर हो।
०39

1
भविष्य में वापस: P
SSpoke

5

अपरिभाषित का मतलब यह नहीं है कि अनुमति नहीं है, इसका मतलब यह हो सकता है कि संकलक कार्यान्वयनकर्ता को यह करने की अधिक स्वतंत्रता है कि वे कैसे चाहते हैं।

उदाहरण के लिए, कुछ आर्किटेक्चर पर यह संभव नहीं हो सकता है - अपरिभाषित उन्हें अभी भी एक अनुरूप 'सी' लाइब्रेरी की अनुमति देता है, भले ही आप ऐसा नहीं कर सकते।


5

एक और समाधान:

POSIX मानकर फ़ंक्शन और डेटा पॉइंटर्स को समान आकार और प्रतिनिधित्व की गारंटी देता है (मुझे इसके लिए पाठ नहीं मिल सकता है, लेकिन ओपी उद्धृत उदाहरण से पता चलता है कि वे कम से कम इस आवश्यकता को बनाने का इरादा रखते हैं), निम्नलिखित काम करना चाहिए:

double (*cosine)(double);
void *tmp;
handle = dlopen("libm.so", RTLD_LAZY);
tmp = dlsym(handle, "cos");
memcpy(&cosine, &tmp, sizeof cosine);

यह char []प्रतिनिधित्व के माध्यम से अलियासिंग नियमों का उल्लंघन करने से बचता है , जिसे सभी प्रकार के उपनाम करने की अनुमति है।

अभी तक एक और दृष्टिकोण:

union {
    double (*fptr)(double);
    void *dptr;
} u;
u.dptr = dlsym(handle, "cos");
cosine = u.fptr;

लेकिन मैं memcpyदृष्टिकोण की सिफारिश करूंगा यदि आप बिल्कुल 100% सही सी चाहते हैं।


5

वे अलग-अलग अंतरिक्ष आवश्यकताओं के साथ विभिन्न प्रकार के हो सकते हैं। किसी को असाइन करना अपरिवर्तनीय रूप से पॉइंटर के मूल्य को स्लाइस कर सकता है ताकि परिणाम वापस कुछ अलग हो।

मेरा मानना ​​है कि वे अलग-अलग प्रकार के हो सकते हैं क्योंकि मानक संभव कार्यान्वयनों को सीमित नहीं करना चाहता है जो अंतरिक्ष को बचाते हैं जब इसकी आवश्यकता नहीं होती है या जब आकार सीपीयू का कारण बन सकता है तो इसका उपयोग करने के लिए अतिरिक्त बकवास करना होगा, आदि ...


3

एकमात्र सही मायने में पोर्टेबल समाधान dlsymकार्यों के लिए उपयोग नहीं करना है , और इसके बजाय फ़ंक्शन dlsymपॉइंटर वाले डेटा को पॉइंटर प्राप्त करने के लिए उपयोग करना है। उदाहरण के लिए, आपकी लाइब्रेरी में:

struct module foo_module = {
    .create = create_func,
    .destroy = destroy_func,
    .write = write_func,
    /* ... */
};

और फिर आपके आवेदन में:

struct module *foo = dlsym(handle, "foo_module");
foo->create(/*...*/);
/* ... */

संयोग से, यह वैसे भी अच्छा डिज़ाइन अभ्यास है, और डायनेमिक लोडिंग के माध्यम से dlopenऔर स्थैतिक लिंकिंग को सभी मॉड्यूल का समर्थन करना आसान बनाता है, जो सिस्टम को डायनेमिक लिंकिंग का समर्थन नहीं करता है, या जहां उपयोगकर्ता / सिस्टम इंटीग्रेटर डायनेमिक लिंकिंग का उपयोग नहीं करना चाहता है।


2
अच्छा! जबकि मैं मानता हूं कि यह अधिक सुव्यवस्थित प्रतीत होता है, यह अभी भी स्पष्ट नहीं है (मेरे लिए) कि मैं इसके ऊपर स्थैतिक लिंकिंग पर कैसे हथौड़ा करता हूं। क्या आप विस्तार से समझा सकते हैं?
मानव

2
यदि प्रत्येक मॉड्यूल की अपनी foo_moduleसंरचना है (अद्वितीय नामों के साथ), तो आप struct { const char *module_name; const struct module *module_funcs; }जिस सारणी को "लोड" करना चाहते हैं और सही पॉइंटर लौटना चाहते हैं, उसके लिए आप इस सारणी को खोजने के लिए एक साधारण फ़ाइल और सरणी के साथ एक अतिरिक्त फ़ाइल बना सकते हैं , फिर इसका उपयोग करें के स्थान पर dlopenऔर dlsym
R .. गिटहब स्टॉप हेल्पिंग ICE

@ आर .. सच है, लेकिन यह मॉड्यूल संरचना को बनाए रखने के द्वारा रखरखाव लागत जोड़ता है।
user877329

3

एक आधुनिक उदाहरण जहां फ़ंक्शन पॉइंटर्स आकार में डेटा बिंदुओं से भिन्न हो सकते हैं: C ++ वर्ग सदस्य फ़ंक्शन पॉइंटर्स

सीधे https://blogs.msdn.microsoft.com/oldnewthing/20040209-00/?p=402013/ से उद्धृत

class Base1 { int b1; void Base1Method(); };
class Base2 { int b2; void Base2Method(); };
class Derived : public Base1, Base2 { int d; void DerivedMethod(); };

अब दो संभावित thisसंकेत हैं।

एक सदस्य फ़ंक्शन के लिए एक सूचक को एक सदस्य फ़ंक्शन के Base1लिए सूचक के रूप में उपयोग किया जा सकता है Derived, क्योंकि वे दोनों एक ही this सूचक का उपयोग करते हैं । लेकिन एक सदस्य फ़ंक्शन के लिए पॉइंटर का Base2उपयोग नहीं किया जा सकता है, जैसा कि एक सदस्य फ़ंक्शन के लिए एक पॉइंटर के रूप में किया जाता है Derived, क्योंकि this पॉइंटर को समायोजित करने की आवश्यकता होती है।

इसे हल करने के कई तरीके हैं। यहां बताया गया है कि विज़ुअल स्टूडियो कंपाइलर इसे कैसे हैंडल करता है:

एक बहु-विरासत वाले वर्ग के सदस्य फ़ंक्शन के लिए एक सूचक वास्तव में एक संरचना है।

[Address of function]
[Adjustor]

एक वर्ग के एक पॉइंटर-टू-मेंबर-फंक्शन का आकार जो कई उत्तराधिकार का उपयोग करता है, एक पॉइंटर का आकार प्लस का आकार है size_t

tl; dr: एकाधिक वंशानुक्रम का उपयोग करते समय, सदस्य फ़ंक्शन के लिए एक पॉइंटर (संकलक, संस्करण, वास्तुकला, आदि के आधार पर) वास्तव में संग्रहीत किया जा सकता है

struct { 
    void * func;
    size_t offset;
}

जो स्पष्ट रूप से एक से बड़ा है void *


2

अधिकांश आर्किटेक्चर पर, सभी सामान्य डेटा प्रकारों के पॉइंटर्स का एक ही प्रतिनिधित्व होता है, इसलिए डेटा पॉइंटर प्रकारों के बीच कास्टिंग एक नो-ऑप है।

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

जैसा कि किसी ने उल्लेख किया है, यदि आपको इसकी आवश्यकता है तो आप इसे संघ का उपयोग करके प्राप्त कर सकते हैं। लेकिन शून्य * के अधिकांश उपयोग केवल डेटा के लिए होते हैं, इसलिए यह केवल एक फ़ंक्शन पॉइंटर को संग्रहीत करने की आवश्यकता होने पर उनकी सभी मेमोरी उपयोग को बढ़ाने के लिए बेहतर होगा।


-1

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


यह उत्तर गलत है। आप उदाहरण के लिए एक फ़ंक्शन पॉइंटर को डेटा पॉइंटर में कनवर्ट कर सकते हैं और उससे पढ़ सकते हैं (यदि आपके पास उस पते से पढ़ने की अनुमति है, हमेशा की तरह)। परिणाम उतना ही समझ में आता है, जितना कि x86 पर।
मैनुअल जैकब
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.