2013-03-14 15 views
5

नीचे कोई आउटपुट प्राप्त व्याख्या कर सकते हैं के साथ आयामी सरणियों मार डाला जब:दो संकेत

0xbf7f6286 
0xbf7f6286 
0xbf7f6292 
0x38 
0x1 
  • & एक [0] का पता है:

    #include<stdio.h> 
    void main() 
    { 
    char a[5][10]={"one","two","three","four","five"}; 
    char **str=a; 
    printf("%p ", &a[0]); 
    printf("\n%p ", &str[0]); 
    printf("\n%p ", &str[3]); 
    printf("\n%p ", &str[1][56]); 
    printf("\n%p ", &(*(*(str+4)+1))); 
    } 
    

    नीचे उत्पादन मनाया है सरणी के शुरुआती पते का

  • & str [0] issame

    01 है

    क्या कोई बता सकता है कि str [3] का पता 0xbf7f6292 है। मेरी समझ के अनुसार & str [0] और & str [3] 30 बाइट्स से भिन्न होना चाहिए।

    कृपया कोई भी बताएं कि अन्य दो मामलों के लिए आउटपुट कैसे।

अग्रिम

+0

आपका सरणी 'A', यहां तक ​​कि दो आयामों के साथ, नहीं सूचक के लिए सूचक का पर्याय है। – WhozCraig

उत्तर

3

पहले दो printf धन्यवाद स्पष्ट रूप से एक ही पते से, यानी दो-डी सरणी के पहले elememt एक एक घ सरणी है कि मुद्रित कर रहे हैं। इसलिए इसमें कोई संदेह नहीं होना चाहिए कि उनमें से दोनों सरणी शुरू करने की ओर इशारा कर रहे हैं, यह एक [0] [0] है।

char ** str एक सूचक है इसलिए जब आप str [3] इंटरनल का मतलब है (str + 3) और शायद आपका कोड 32 बिट मशीन पर चल रहा है तो यह सूचक के अनुसार 12 बाइट आगे बढ़ेगा airthematic। सरणी के साथ भ्रमित न हो, str एक 2-डी पॉइंटर एक सरणी नहीं है।

0xbf7f6286 str [0] 0xbf7f6292 str [3] diff 0xc है जो अपेक्षा के अनुसार सही है।

printf ("\ n% p", & str [1] [56]); printf ("\ n% p", & (( (str + 4) +1));

दो अंतिम बयान के ऊपर आप पॉइंटर एयरटेमेटिक के गलत गलती के कारण प्रिंट करने की कोशिश कर रहे थे।

मुझे आशा है कि इससे आपके संदेह को हल करने में मदद मिलेगी।

+0

मुझे कोई संदेह है: एक 2 डी सरणी के रूप में यह 2 डी सरणी के पते को शुरू करने के लिए इंगित करता है। लेकिन, str एक सूचक के लिए एक सूचक है। जैसा कि हमने str = कॉपी किया है, इसलिए एक पॉइंटर वैरिएबल स्ट्र में ए और पॉइंटर वेरिएबल स्ट्र का पता कुछ स्थान 'x' पर बनाया जाएगा। इसलिए, जब हम प्रिंट करते हैं और str [0] इसे दाईं ओर के पते के बजाय 'x' प्रिंट करना चाहिए? अगर कहीं गलत है – user2165725

0

जब आप str + 3 कहते हैं, तो इसका मतलब है str + (3 * sizeof (char **))। तो यदि 'char **' का आकार 4bytes है तो str 12 बाइट्स द्वारा बढ़ाया जाता है।

0

आप एक 2 डी सरणी, आप दो छोरों का उपयोग करना चाहिए (मैं छोरों 'के लिए' दो सुझाव है) का प्रिंट लें और एक दूसरे के अंदर नेस्ट करना चाहते हैं।

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

आप सरणी एक बराबर str के लिए स्थापित कर रहे हैं के बाद से, आप वास्तव में पता करने के लिए सूचक str स्थापित कर रहे हैं कि सरणी के लिए 'एक' अंक।

जब आप [int n], उपयोग ऑपरेटर का उपयोग, आप वास्तव में आपके कंप्यूटर को बता रहे हैं अतीत जहां सरणी (या एक सूचक) वास्तव में के लिए अंक 'एन' पता स्लॉट्स देखने के लिए। इस प्रकार यह शीर्ष दो से 16 अधिक क्यों है।

हेक्स की तरह आखिरी दो देखो, मैं उन अंतिम दो आउटपुट लाइनों के बारे में पूरी तरह से निश्चित नहीं हूं।

1
char a[5][10]={"one","two","three","four","five"}; 
char **str=a; 

मुझे कैसे एक दो शुरुआत सूचक काम करता है के बारे में बताएं, और एक सरणी के साथ अपने संबंधों, क्या है ताकि आप सवाल अपने आप को हल करने की कोशिश का एक बेहतर मौका हो सकता है।

पारंपरिक रूप से डबल स्टार पॉइंटर्स को अन्य सूचक के "पता" के साथ असाइन किया गया है।

उदाहरण

char a = 5; 
char *b = &a; 
char **c = &b; 

इसलिए जब आप को डी-संदर्भ एक बार

printf("\n%d",*c) 

ग, आप मूल्य 'बी', जो लेकिन का पता कुछ भी नहीं है द्वारा निहित हो जाएगी 'एक' ।

जब आप को डी-संदर्भ ग दो बार

printf("\n%d",**c) 

, आप 'एक' सीधे का मूल्य मिल जाएगा।

अब सरणी और पॉइंटर्स के बीच संबंधों में आ रहा है।

एक सरणी का नाम इसके पते का समानार्थी है, जिसका अर्थ है कि सरणी का नाम स्वयं का स्थान है।

उदाहरण:

int array [5] = {1,2,3,4,5}; 
printf("\n%p",array); 
printf("\n%p",&array); 
printf("\n%p",&array[0]); 

उन सभी को एक ही पते प्रिंट होगा।

इसलिए क्योंकि ** str एक सूचक एक पते जोत के एक पते की उम्मीद है, यह एक अमान्य सूचक काम

char **str=a; 

है, लेकिन आप अभी केवल एक पते से गुजर रहे हैं [सरणी के नाम = एक]।

तो संक्षेप में, दो सितारा सूचक, दो आयामी सरणी को संभालने के लिए उपयोग नहीं किया जाना चाहिए।

यह question एक सूचक के साथ दो आयामी सरणी को संभालने के साथ सौदा करता है।

8

बहुत कम उत्तर:

बदलें इस:

char **str; 

इस होने के लिए:

char (*str)[10]; 

और उसके बाद अपने कोड में कोई भी त्रुटि ठीक। str का प्रकार आपके दो आयामी सरणी के प्रकार के साथ संगत नहीं है। मैंने रिटर्न प्रकार को intmain() (सी मानक के अनुसार शून्य की अनुमति नहीं है) को ठीक करने की स्वतंत्रता ली।मैं भी स्रोत जहां तकनीकी रूप से अपरिभाषित व्यवहार है चिह्नित:

#include <stdio.h> 
int main() 
{ 
    char a[5][10]={"one","two","three","four","five"}; 
    char (*str)[10] = a; 
    printf("%p ", &a[0]); 
    printf("\n%p ", &str[0]); 
    printf("\n%p ", &str[3]); 
    printf("\n%p ", &str[1][56]);  // this is undefined behaviour 
    printf("\n%p ", &(*(*(str+4)+1))); 
    return 0; 
} 

तुम भी ध्यान देना चाहिए पिछले printf() बयान एक ज़रूरत से ज़्यादा &(*(...)) कि जरूरत नहीं है है। यह इस तरह से देखने के लिए है, जो बराबर है छीन लिया जा सकता है:

printf("\n%p ", *(str+4)+1); 

आउटपुट (सिस्टम आश्रित)

0x7fff5fbff900 
0x7fff5fbff900 
0x7fff5fbff91e 
0x7fff5fbff942 
0x7fff5fbff929 

बहुत (बहुत, बहुत) लांग उत्तर

आप अपनी धारणा में गलत हैं कि दो आयाम अल सरणी एक पॉइंटर-टू-पॉइंटर के बराबर है। वे केवल उपयोग वाक्यविन्यास में समान हैं। निम्नलिखित कैसे सरणी a स्मृति में चित्रित किया है

char a[5][10] 

    0 1 2 3 4 5 6 7 8 9 
    ----------------------------------------- 
0 | o | n | e | 0 | | | | | | | 
    ----------------------------------------- 
1 | t | w | o | 0 | | | | | | | 
    ----------------------------------------- 
2 | t | h | r | e | e | 0 | | | | | 
    ----------------------------------------- 
3 | f | o | u | r | 0 | | | | | | 
    ----------------------------------------- 
4 | f | i | v | e | 0 | | | | | | 
    ----------------------------------------- 

ध्यान दें कि दूसरी पंक्ति के शुरू करने से पता है, जो &a[1] है, दस बाइट्स पिछले पहली पंक्ति के शुरू पता है का एक मोटा लेआउट है, &a[0] (जो पूरी तरह से सरणी का प्रारंभिक पता नहीं है)। निम्नलिखित को दर्शाता है यह:

int main() 
{ 
    char a[5][10] = { "one", "two", "three", "four", "five" }; 

    printf("&a = %p\n", &a); 
    printf("&a[0] = %p\n", &a[0]); 
    printf("a[0] = %p\n", a[0]); 
    printf("&a[1] = %p\n", &a[1]); 
    printf("a[1] = %p\n", a[1]); 
    return 0; 
} 

आउटपुट

&a = 0x7fff5fbff8e0 
&a[0] = 0x7fff5fbff8e0 
a[0] = 0x7fff5fbff8e0 
&a[1] = 0x7fff5fbff8ea 
a[1] = 0x7fff5fbff8ea 

ध्यान दें कि a[1] पर पता 10 बाइट (0x0a हेक्स) सरणी की शुरुआत पिछले है। पर क्यों? इसका उत्तर देने के लिए टाइप किए गए पॉइंटर अंकगणित की मूल बातें समझनी चाहिए।


कैसे करता है टाइप-सूचक अंकगणित काम?

सी और सी ++ में, void को छोड़कर सभी पॉइंटर्स टाइप किए गए हैं। पॉइंटर से जुड़े एक मौलिक डेटा प्रकार है। उदाहरण के लिए। पाँच पूर्णांकों का आकार होने के लिए आबंटित स्मृति का एक क्षेत्र है

int *iptr = malloc(5*sizeof(int)); 

iptr ऊपर अंक। यह संबोधित करते के रूप में हम आम तौर पर सरणियों को संबोधित इस तरह दिखता है:

iptr[1] = 1; 

लेकिन हम बस के रूप में आसानी से इस तरह उसे संबोधित कर सकते हैं:

*(iptr+1) = 1; 

और परिणाम एक ही हो सकता है; दूसरे सरणी स्लॉट में मान 1 का भंडारण (0-स्लॉट पहले होना)। हम जानते हैं कि dereference ऑपरेटर * किसी पते (सूचक में संग्रहीत पता) के माध्यम से पहुंच की अनुमति देता है। लेकिन (iptr+1) कैसे चार बाइट्स को छोड़ना जानता है (यदि आपका int प्रकार 32 बिट है, तो आठ बिट्स यदि वे 64 बिट हैं) तो अगले पूर्णांक स्लॉट तक पहुंचने के लिए?

उत्तर: टाइप-पॉइंटर अंकगणित की वजह से।संकलक जानता है कि कितने चौड़े, बाइट्स में अंतर्निहित सूचक पॉइंटर है (इस मामले में, int प्रकार की चौड़ाई)। जब आप पॉइंटर्स से/स्केलर मानों को जोड़ते या घटाते हैं, तो संकलक उचित "कोड" के लिए उपयुक्त कोड उत्पन्न करता है। यह उपयोगकर्ता प्रकारों के साथ भी काम करता है। यह नीचे दर्शाया गया है:

#include <stdio.h> 

typedef struct Data 
{ 
    int ival; 
    float fval; 
    char buffer[100]; 
} Data; 

int main() 
{ 
    int ivals[10]; 
    int *iptr = ivals; 
    char str[] = "Message"; 
    char *pchr = str; 
    Data data[2]; 
    Data *dptr = data; 

    printf("iptr = %p\n", iptr); 
    printf("iptr+1 = %p\n", iptr+1); 

    printf("pchr = %p\n", pchr); 
    printf("pchr+1 = %p\n", pchr+1); 

    printf("dptr = %p\n", dptr); 
    printf("dptr+1 = %p\n", dptr+1); 

    return 0; 
} 

आउटपुट

iptr = 0x7fff5fbff900 
iptr+1 = 0x7fff5fbff904 
pchr = 0x7fff5fbff8f0 
pchr+1 = 0x7fff5fbff8f1 
dptr = 0x7fff5fbff810 
dptr+1 = 0x7fff5fbff87c 

सूचना iptr और iptr+1 के बीच पता अंतर नहीं एक बाइट है; यह चार बाइट्स है (मेरे सिस्टम पर int की चौड़ाई)। इसके बाद, एक char की चौड़ाई pchr और pchr+1 के साथ एक बाइट के रूप में प्रदर्शित की जाती है। अंत में, हमारे कस्टम डेटा प्रकार Data इसके दो सूचक मान, dptr और dptr+1 दिखाते हैं कि यह 0x6C, या 108 बाइट चौड़ा है। (यह संरचना पैकिंग और फील्ड संरेखण के कारण बड़ा हो सकता था, लेकिन हम इस उदाहरण के साथ भाग्यशाली थे कि यह नहीं था)। यह समझ में आता है, क्योंकि संरचना में दो 4-बाइट डेटा फ़ील्ड्स (int और float) और एक चार बफर 100 तत्व चौड़े हैं।

रिवर्स भी सच है, और अक्सर अनुभवी सी/सी ++ प्रोग्रामर द्वारा विचार किया जाता है। यह टाइप किया गया पॉइंटर differencing है। आप एक निर्दिष्ट प्रकार के दो वैध संकेत दिए गए वैध स्मृति का एक सन्निहित क्षेत्र के भीतर है:

int ar[10]; 
int *iptr1 = ar+1; 
int *iptr5 = ar+5; 

क्या करें क्या आप से एक परिणाम के रूप मिलता है लगता है:

printf("%lu", iptr5 - iptr1); 

जवाब है .. 4। हूपे, आप कहते हैं। कोई बड़ी बात नहीं? क्या आपको विश्वास नहीं है कि यह है। यह अत्यंत एक विशिष्ट तत्व के बफर में ऑफसेट निर्धारित करने के लिए पॉइंटर अंकगणितीय का उपयोग करते समय आसान है।

सारांश में, आप इस तरह एक अभिव्यक्ति है जब:

int ar[5]; 
int *iptr = ar; 

iptr[1] = 1; 

आप यह पता कर सकते हैं के बराबर है:

*(iptr+1) = 1; 

कौन सा, सामान्य-आदमी की दृष्टि से, का अर्थ है "पता ले लो iptr चर में आयोजित, 1 * (int बाइट्स में बाइट्स) बाइट्स जोड़ें, फिर लौटाए गए पते के माध्यम से स्मृति स्मृति में 1 को संग्रहीत करें। "

साइट बार: यह भी काम करेगा।यदि आप के बारे में सोच क्यों

1[iptr] = 1; 

अपने (हमारे) नमूना सरणी वापस, अब क्या होता है जब हम a का एक ही पते का उल्लेख पर एक नज़र ले जा सकते हैं देखते हैं, लेकिन एक डबल सूचक के माध्यम से (जो पूरी तरह से गलत है और अपने संकलक आप काम के बारे में कम से कम चेतावनी दी है चाहिए):

char **str = a; // Error: Incompatible pointer types: char ** and char[5][10] 

खैर वह काम नहीं करता। लेकिन ऐसा करने के लिए एक पल के लिए मान लें। char ** एक पॉइंटर-टू-चार के लिए एक सूचक है। इसका मतलब यह है कि परिवर्तनीय स्वयं को पॉइंटर से ज्यादा कुछ नहीं रखता है। इसमें आधार पंक्ति चौड़ाई आदि की कोई अवधारणा नहीं है, इसलिए मानते हैं कि आप डबल-पॉइंटर str में a का पता डालते हैं।

char **str = (char **)(a); // Should NEVER do this, here for demonstration only. 
char *s0 = str[0]; // what do you suppose this is? 

हमारे परीक्षण कार्यक्रम के लिए थोड़ा सा अद्यतन:

int main() 
{ 
    char a[5][10] = { "one", "two", "three", "four", "five" }; 
    char **str = (char **)a; 
    char *s0 = str[0]; 
    char *s1 = str[1]; 

    printf("&a = %p\n", &a); 
    printf("&a[0] = %p\n", &a[0]); 
    printf("a[0] = %p\n", a[0]); 
    printf("&a[1] = %p\n", &a[1]); 
    printf("a[1] = %p\n", a[1]); 

    printf("str = %p\n", str); 
    printf("s0 = %p\n", s0); 
    printf("s1 = %p\n", s1); 

    return 0; 
} 

हमें निम्न परिणाम देता है:

&a = 0x7fff5fbff900 
&a[0] = 0x7fff5fbff900 
a[0] = 0x7fff5fbff900 
&a[1] = 0x7fff5fbff90a 
a[1] = 0x7fff5fbff90a 
str = 0x7fff5fbff900 
s0 = 0x656e6f 
s1 = 0x6f77740000 

खैर, str लगता है कि यह हम क्या चाहते है, लेकिन यह बात क्या है s0 में? क्यों, अक्षरों के लिए ASCII चरित्र मान है। कौन सा? एक सभ्य ASCII table की एक त्वरित जांच से पता चलता हैं:

0x65 : e 
0x6e : n 
0x6f : o 

शब्द "एक" रिवर्स में (रिवर्स अपने सिस्टम पर बहु ​​बाइट मूल्यों की endian से निपटने के कारण होता है, लेकिन मुझे आशा है कि समस्या यह है Thats स्पष्ट क्या है कि दूसरी मूल्य के बारे में:।

0x6f : o 
0x77 : w 
0x74 : t 

हाँ, thats "दो" तो क्यों न हम संकेत के रूप में हमारे सरणी में बाइट्स हो रही है

हममम .. हाँ, के रूप में मैं में कहा।? मेरी टिप्पणी। जो भी आपको या हिन बताता है आपको बताता है कि डबल पॉइंटर्स और दो आयामी सरणी समानार्थी गलत हैं। टाइप किए गए सूचक अंकगणित में हमारे अंतराल को याद करें। याद रखें, यह:

str[1] 

और इस:

*(str+1) 

पर्याय बन गया है। कुंआ। सूचक के टाइप करें? जिस प्रकार से यह इंगित करता है वह char पॉइंटर है। इसलिए, इस बीच बाइट गिनती अंतर:

str + 0 

और इस

str + 1 

एक char* के आकार होगा। मेरे सिस्टम पर जो 4 बाइट्स है (मेरे पास 32 बिट पॉइंटर्स हैं)। यह बताता है कि क्यों पताstr[1] में डेटा हमारे मूल सरणी के आधार पर चार बाइट्स है।

इसलिए, अपने पहले मौलिक प्रश्न का उत्तर दें (हां, हम अंततः उस पर पहुंचे)। यह:

&str[3] 

इस के बराबर है:

0xbf7f6292

जवाब क्यों str[3] है

(str + 3) 

लेकिन हम (str + 3) ऊपर से पता अभी पता str में जमा हो जाती है, फिर str अंक की 3x चौड़ाई जोड़ना, जोहै, उस पते पर बाइट्स में। कुंआ। हम जानते हैं से अपने दूसरे printf क्या है कि पता है:

0xbf7f6286 

और हम जानते हैं कि आपके सिस्टम पर एक सूचक की चौड़ाई 4 बाइट (32 बिट संकेत) है। इसलिए ...

0xbf7f6286 + (3 * 4) 

या ....

0xbf7f6286 + 0x0C = 0xbf7f6292 
+1

अद्भुत जवाब कृपया विस्तृत करें !! इस तरह के उत्तर में आने के लिए ओपी के पास एक शानदार दिन होना चाहिए, मैं इस पोस्ट के लिए पसंदीदा होगा, यह प्रश्न !!! –

+0

उत्कृष्ट उत्तर ... वास्तव में उपयोगी – user2165725

+0

@ user2165725, अगर आपको कोई उत्तर उपयोगी लगता है, तो आप जवाब स्वीकार कर ऐसा कहते हैं –

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