शून्य पॉइंटर्स शून्य होने का नाटक करने वाले शून्य पॉइंटर्स

मैं कुछ सोच रहा हूँ। मुझे सीधे इस सवाल का जवाब देने में कुछ भी नहीं मिला है, लेकिन मुझे लगता है कि मुझे जवाब पता है; मैं बस कुछ और अनुभवी व्यक्तियों से कुछ इनपुट चाहता हूं।

नोंस:

एक शून्य पॉइंटर केवल एक स्मृति पते के लिए इंगित करता है। इसमें कोई प्रकार की जानकारी शामिल नहीं है।

एक int सूचक एक int युक्त स्मृति पते को इंगित करता है। मूल रूप से पते में जो कुछ भी भरा हुआ था, इस पर ध्यान दिए बिना, यह एक पूर्णांक के रूप में इंगित स्मृति पते में जो कुछ भी है, उसे पढ़ेगा।

सवाल:

यदि एक शून्य डबल पॉइंटर शून्य ** foo शून्य पॉइंटर्स की गतिशील आवंटित सरणी को इंगित करना था

void ** foo = malloc(sizeof(void *) * NUM_ELEMENTS);

क्या यह सच है, क्योंकि मुझे लगता है कि शून्य पॉइंटर्स की अनूठी प्रकृति की वजह से वास्तव में किसी भी तरह की प्रकार की जानकारी की कमी है कि void ** foo के बराबर बयान होगा

void * bar = malloc(sizeof(void *) * NUM_ELEMENTS);

और जब मैं एक विशिष्ट प्रकार को निर्दिष्ट करके एक्सेस करने के लिए संकेत का उपयोग करता हूं, जैसे कि

(यह इंगित किया गया था कि मैं शून्य पॉइंटर्स को कम नहीं कर सकता। प्रश्न के उद्देश्य के लिए स्पष्टता के लिए अगली पंक्ति उस जानकारी के लिए उपयुक्त हो गई है)

int ** fubar = bar;

कि मुझे एकल शून्य पॉइंटर से उचित सूचक मिलेगा जो double सूचक की तरह अभिनय है?

या यह सब सिर्फ मेरे सिर में है?

0
Int ** fubar = bar का उपयोग करने के बारे में क्या; ?
जोड़ा लेखक ciphermagi, स्रोत
आप एक शून्य सूचक को कम नहीं कर सकते हैं। मुझे नहीं लगता कि आप कंपाइलर एक्सटेंशन की अनुपस्थिति में पॉइंटर अंकगणित भी कर सकते हैं। आपको इसे पहले डालना होगा।
जोड़ा लेखक user2357112, स्रोत
काम कर सकता है, मुझे लगता है, जब तक आप इसे कम करने की कोशिश नहीं करते। यदि आप गलत प्रकार के लिए पॉइंटर डालते हैं और इसे कम करने की कोशिश करते हैं, तो ऑपरेशन को समझने की कोशिश करने के लिए सी रूपांतरण नहीं करेगा। आपको अपरिभाषित व्यवहार मिलेगा।
जोड़ा लेखक user2357112, स्रोत

2 उत्तर

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

void * bar = malloc(sizeof(int*) * NUM_ELEMENTS);
int ** fubar = bar;

तो सब ठीक हो जाएगा।

अब मशीनों की विशाल बहुमत पर, एक int * और शून्य * वास्तव में का आकार और संरेखण होगा। तो आपके कोड को अभ्यास में ठीक काम करना चाहिए।

इसके अतिरिक्त, ये दोनों बराबर नहीं हैं:

void ** foo = malloc(sizeof(void *) * NUM_ELEMENTS);
void * bar = malloc(sizeof(void *) * NUM_ELEMENTS);

ऐसा इसलिए है क्योंकि शून्य कोड प्राप्त करने के लिए किसी भी तत्व पर foo को संदर्भित किया जा सकता है, जबकि bar नहीं हो सकता है। उदाहरण के लिए, यह प्रोग्राम सही है और मेरी 32-बिट मशीन पर 00000000 प्रिंट करता है:

#include 
#include 

int main(void) 
{
  void **a = calloc(10, sizeof(void*));
  printf("%p\n", a[0]);
  return 0;
}

एक अन्य बात यह है कि आप सोच रहे हैं कि मशीन स्तर पर पॉइंटर में टाइप जानकारी स्पष्ट है। यह सच नहीं है (कम से कम कार्यान्वयन के विशाल बहुमत के लिए)। सी पॉइंटर्स का प्रकार आमतौर पर केवल तभी प्रदर्शित होता है जब प्रोग्राम संकलित किया जा रहा है। संकलन के समय तक, डीबगिंग प्रतीक तालिकाओं को छोड़कर स्पष्ट प्रकार की जानकारी सामान्य रूप से खो जाती है, जो रनने योग्य कोड नहीं होते हैं। (इसमें कुछ मामूली अपवाद हैं। और सी ++ के लिए स्थिति बहुत अलग है।)

0
जोड़ा
मुझे विश्वास है कि आप जो पूछ रहे हैं उसके बारे में आप उलझन में हैं।
जोड़ा लेखक ciphermagi, स्रोत

malloc के परिणाम को शून्य * ऑब्जेक्ट में असाइन करने की अनुमति है और उसके बाद इसे बाद में int ** ऑब्जेक्ट पर असाइन करें। ऐसा इसलिए है क्योंकि malloc के वापसी मान में शून्य * टाइप किया गया है, और यह मौलिक संरेखण आवश्यकता के साथ किसी भी प्रकार के ऑब्जेक्ट को पॉइंटर असाइन करने के लिए उपयुक्त होने की गारंटी है।

हालांकि, यह कोड:

#define NUM_ELEMENTS 1000
void *bar = malloc(sizeof(void *) * NUM_ELEMENTS);
int **fubar = bar;
*fubar = 0;

काम करने के लिए सी मानक द्वारा गारंटी नहीं है; यह अपरिभाषित व्यवहार हो सकता है। इसका कारण स्पष्ट नहीं है। सी मानक को एक ही आकार के लिए विभिन्न प्रकार के पॉइंटर्स की आवश्यकता नहीं होती है। एक सी कार्यान्वयन int * के आकार को एक लाख बाइट्स और शून्य * के आकार को चार बाइट्स पर सेट कर सकता है। इस मामले में, 1000 शून्य * के लिए आवंटित स्थान एक int * को पकड़ने के लिए पर्याप्त नहीं होगा, इसलिए * fubar को असाइनमेंट अपरिभाषित है व्यवहार। आम तौर पर, कोई व्यक्ति केवल एक बिंदु साबित करने के लिए सी को लागू करेगा। हालांकि, छोटे पैमाने पर समान त्रुटियां संभव हैं: सी कार्यान्वयन हैं जिसमें विभिन्न प्रकार के पॉइंटर्स के विभिन्न आकार होते हैं।

किसी ऑब्जेक्ट प्रकार के लिए पॉइंटर को किसी ऑब्जेक्ट प्रकार में पॉइंटर में परिवर्तित किया जा सकता है बशर्ते पॉइंटर के पास गंतव्य प्रकार के लिए उपयुक्त संरेखण हो। यदि ऐसा होता है, तो इसे वापस परिवर्तित करने से मूल मूल्य के साथ एक सूचक उत्पन्न होता है। इस प्रकार, आप पॉइंटर्स को void * में पॉइंटर्स में शून्य और वापस करने के लिए परिवर्तित कर सकते हैं, और आप पॉइंटर्स को शून्य * में पॉइंटर्स को int * और वापस, बशर्ते संरेखण उपयुक्त हों (जो वे होंगे यदि पॉइंटर्स malloc द्वारा लौटाए गए थे और आप विस्तारित संरेखण के साथ कस्टम ऑब्जेक्ट्स का उपयोग नहीं कर रहे हैं)।

आम तौर पर, आप एक ऑब्जेक्ट प्रकार के लिए पॉइंटर का उपयोग करके नहीं लिख सकते हैं और फिर एक पॉइंटर का उपयोग किसी ऑब्जेक्ट प्रकार पर एक ही बाइट्स को पढ़ सकते हैं। यह एलियासिंग नियमों का उल्लंघन करता है। एक अपवाद यह है कि यदि पॉइंटर्स में से एक चरित्र प्रकार के लिए है। इसके अलावा, कई सी कार्यान्वयन इस तरह के अलियासिंग का समर्थन करते हैं, लेकिन इस तरह के समर्थन को सक्षम करने के लिए इसे कमांड लाइन विकल्पों को सेट करने की आवश्यकता हो सकती है।

एलियासिंग पर इस निषेध में पॉइंटर्स को दोबारा परिभाषित करना शामिल है। इस कोड पर विचार करें:

int a;
int *b = &a;
void **c = (void **) &b;
void *d = *c;
int *e = (int *) d;

चौथी पंक्ति में, c बाइट्स को इंगित करता है जो b पर कब्जा करता है लेकिन * c उन बाइट्स को शून्य * के रूप में समझने का प्रयास करता है । यह काम करने की गारंटी नहीं है, इसलिए d प्राप्त होने वाला मान a के लिए एक सूचक नहीं है, भले ही इसे int * में परिवर्तित किया गया हो आखिरी पंक्ति में।

0
जोड़ा
ठीक है, फिर आगे बढ़ने के लिए, शून्य * foo = malloc (sizeof (int *) * NUM) का परिणाम क्या होगा; और फिर बाद में int ** बार = foo में कास्टिंग; और इसके बाद (...) * (बार + i) = malloc (sizeof (int) * LEN) ;?
जोड़ा लेखक ciphermagi, स्रोत
तो फिर प्रभावी रूप से शून्य ** foo या शून्य *** बार या कुछ भी समान होने का कोई उद्देश्य नहीं है, क्योंकि शून्य * फ्यूबर एक ही काम कर सकता है जब तक यह सही सूचक को सही ढंग से वापस लाया जाता है
जोड़ा लेखक ciphermagi, स्रोत
lol ... मुझे वह मिलता है। जहां तक ​​मॉलोक के परिणाम को असाइन किया गया है, यह पुस्तकालयों की पोर्टेबिलिटी के बारे में अधिक है जो विभिन्न डेटा प्रकारों को संभालने में सक्षम होना चाहते हैं और केवल शून्य पॉइंटर्स को वापस लौटाना चाहते हैं।
जोड़ा लेखक ciphermagi, स्रोत
@ciphermagi "तो फिर प्रभावी ढंग से शून्य होने में कोई उद्देश्य नहीं है **" - बेशक वहाँ है! एक शून्य ** का उद्देश्य शून्य * को इंगित करना है।
जोड़ा लेखक immibis, स्रोत
@ जोनाहेलसन: यह बिल्कुल ठीक है, जहां तक ​​सी मानक का संबंध है। malloc एक शून्य * देता है, इसलिए foo को असाइनमेंट केवल उस मूल्य को याद किए बिना याद करता है। फिर int ** bar को foo के साथ प्रारंभ करना bar को उसी मान पर सेट करता है जैसे सामान्य int ** bar = malloc ( ...) होगा।
जोड़ा लेखक Eric Postpischil, स्रोत
@ जोनाहेलसन: विभिन्न प्रकारों में एक प्रमुख उद्देश्य यह दिखाने के लिए है कि आप डेटा का उपयोग कैसे करना चाहते हैं ताकि संकलक आपके उपयोग की जांच कर सकें और त्रुटियों को पकड़ सकें जब आप अपने आवेदन को तैनात किए जाने के बाद कई वर्षों के बाद संकलित करते हैं और एक त्रुटि जो पहले नहीं पकड़ी गई थी आग शुरू करता है आप चाहिए के लिए आवंटित स्थान का उपयोग करने के इरादे के लिए malloc के परिणाम को एक पॉइंटर पर असाइन करने के लिए।
जोड़ा लेखक Eric Postpischil, स्रोत