कैसे एक सरणी का पता C में इसके मूल्य के बराबर है?


189

निम्नलिखित बिट कोड में, पॉइंटर मान और पॉइंटर पते अपेक्षा के अनुसार भिन्न होते हैं।

लेकिन सरणी मान और पते नहीं!

यह कैसे हो सकता है?

उत्पादन

my_array = 0022FF00
&my_array = 0022FF00
pointer_to_array = 0022FF00
&pointer_to_array = 0022FEFC
#include <stdio.h>

int main()
{
  char my_array[100] = "some cool string";
  printf("my_array = %p\n", my_array);
  printf("&my_array = %p\n", &my_array);

  char *pointer_to_array = my_array;
  printf("pointer_to_array = %p\n", pointer_to_array);
  printf("&pointer_to_array = %p\n", &pointer_to_array);

  printf("Press ENTER to continue...\n");
  getchar();
  return 0;
}

Comp.lang.c से अक्सर पूछे जाने वाले प्रश्न: - [तो सी में संकेत और सरणियों के `तुल्यता 'से क्या तात्पर्य है? ] ( C-faq.com/aryptr/aryptrequiv.html ) - [के बाद से सरणी संदर्भ संकेत में क्षय, अगर आगमन एक सरणी है, क्या आगमन और और आगमन के बीच का अंतर है? ] ( c-faq.com/aryptr/aryvsadr.html ) या पूरे ऐरे और पॉइंटर्स सेक्शन को पढ़ें ।
jamesdlin

3
मैंने दो साल पहले इस प्रश्न के आरेख के साथ एक उत्तर जोड़ा था कि यहां क्या sizeof(&array)वापसी है?
बृजेश चौहान

जवाबों:


212

एक सरणी का नाम आमतौर पर सरणी के पहले तत्व के पते का मूल्यांकन करता है, इसलिए arrayऔर &arrayइसका एक ही मूल्य है (लेकिन विभिन्न प्रकार, इसलिए array+1और &array+1यह बराबर नहीं होगा यदि सरणी 1 तत्व से अधिक लंबी है)।

इसके दो अपवाद हैं: जब सरणी नाम एक ऑपरेंड sizeofया यूरीरी &(पता-का) होता है, तो नाम सरणी ऑब्जेक्ट को संदर्भित करता है। इस प्रकार sizeof arrayआपको संपूर्ण सरणी के बाइट्स में आकार मिलता है, न कि एक सूचक का आकार।

के रूप में परिभाषित एक सरणी के लिए T array[size], यह प्रकार होगा T *। जब / यदि आप इसे बढ़ाते हैं, तो आप सरणी में अगले तत्व को प्राप्त करते हैं।

&arrayएक ही पते का मूल्यांकन करता है, लेकिन एक ही परिभाषा को देखते हुए, यह प्रकार का एक संकेतक बनाता है T(*)[size]- यानी, यह एक सरणी के लिए एक संकेतक है, एक तत्व के लिए नहीं। यदि आप इस पॉइंटर को बढ़ाते हैं, तो यह संपूर्ण सरणी का आकार जोड़ देगा, न कि किसी एक तत्व का आकार। उदाहरण के लिए, इस तरह कोड के साथ:

char array[16];
printf("%p\t%p", (void*)&array, (void*)(&array+1));

हम दूसरे पॉइंटर को पहले से 16 से अधिक होने की उम्मीद कर सकते हैं (क्योंकि यह 16 वर्ण की एक सरणी है)। चूंकि% p आमतौर पर हेक्साडेसिमल में पॉइंटर्स को परिवर्तित करता है, इसलिए यह कुछ इस तरह दिखाई दे सकता है:

0x12341000    0x12341010

3
@Alexandre: &arrayसरणी के पहले तत्व के लिए एक संकेतक है, जहां arrayपूरे सरणी को संदर्भित करता है। तुलना sizeof(array)करने से मौलिक अंतर भी देखा जा सकता है sizeof(&array)। हालांकि ध्यान दें कि यदि आप arrayकिसी फ़ंक्शन के तर्क के रूप में पास करते हैं, तो केवल &arrayवास्तव में पारित किया जाता है। जब तक कि इसे एनकैप्सुलेटेड नहीं किया जाता है तब तक आप किसी ऐरे को पास नहीं कर सकते struct
क्लिफर्ड

14
@ क्लिफर्ड: यदि आप किसी फ़ंक्शन को एरे पास करते हैं, तो यह एक पॉइंटर को उसके पहले एलिमेंट के लिए प्रभावी रूप &array[0]से पास करता है, &arrayजो कि एरे का पॉइंटर होगा , न कि प्रभावी तरीके से । यह एक नाइट-पिक हो सकता है लेकिन मुझे लगता है कि इसे स्पष्ट करना महत्वपूर्ण है; संकलक चेतावनी देगा कि यदि फ़ंक्शन में एक प्रोटोटाइप है जो पास किए गए पॉइंटर के प्रकार से मेल खाता है
सीबी बेली

2
@ जेरी कॉफिन उदाहरण के लिए int * p = & a, अगर मुझे इंट पीटर पी का मेमोरी एड्रेस चाहिए, तो मैं पी और पी कर सकता हूं। चूंकि और सरणी पूरे सरणी के पते (जो पहले तत्व के पते पर शुरू होता है) में परिवर्तित हो जाता है। फिर मुझे ऐरे पॉइंटर (जो एरे में पहले एलीमेंट का पता स्टोर करता है) का मेमोरी एड्रेस कैसे मिलेगा? यह स्मृति में कहीं होना चाहिए सही है?
जॉन ली

2
@ जॉननी: नहीं, स्मृति में कहीं भी सरणी के लिए सूचक नहीं है। यदि आप एक पॉइंटर बनाते हैं, तो आप उसका पता ले सकते हैं int *p = array; int **pp = &p;:।
जेरी कॉफिन

3
@ पहले की टिप्पणी गलत है, फिर भी इसे क्यों रखा? मुझे लगता है कि यह उन लोगों के लिए गलतफहमी पैदा कर सकता है जो निम्नलिखित (@Charles) उत्तर नहीं पढ़ते हैं।
रिक

30

ऐसा इसलिए है क्योंकि सरणी नाम ( my_array) एक सूचक से सरणी के लिए अलग है। यह एक सरणी के पते के लिए एक उपनाम है, और इसके पते को सरणी के पते के रूप में परिभाषित किया गया है।

सूचक स्टैक पर एक सामान्य सी चर है, हालांकि। इस प्रकार, आप इसका पता ले सकते हैं और इसके अंदर रखे पते से एक अलग मूल्य प्राप्त कर सकते हैं।

मैंने इस विषय के बारे में यहाँ लिखा है - कृपया एक बार देख लें।


नहीं होना चाहिए क्योंकि my_array का मूल्य स्टैक पर नहीं है, केवल my_array [0 ... लंबाई] हैं, क्योंकि my_array एक अमान्य ऑपरेशन है? तब यह सब समझ में आता है ...
एलेक्जेंड्रे

@ एलेक्सैंड्रे: मुझे यकीन नहीं है कि इसकी अनुमति क्यों है, वास्तव में।
एली बेंडरस्की

आप किसी भी चर का पता ले सकते हैं (यदि चिह्नित नहीं है register) तो उसकी भंडारण अवधि जो भी हो: स्थिर, गतिशील या स्वचालित।
सीबी बेली

my_arrayही है, ढेर पर है, क्योंकि my_array है पूरे सरणी।
कैफे

3
my_array, जब संचालक &या sizeofसंचालक का विषय नहीं होता है, तो उसके पहले तत्व (यानी। &my_array[0]) के लिए एक पॉइंटर का मूल्यांकन किया जाता है - लेकिन my_arrayस्वयं वह पॉइंटर नहीं है ( my_arrayअभी भी एरे है)। वह पॉइंटर सिर्फ एक अल्पकालिक गतिरोध है (उदाहरण के लिए int a;, यह बस की तरह है a + 1) - वैचारिक रूप से कम से कम यह "आवश्यकतानुसार गणना" है। वास्तविक "मान" my_arrayसंपूर्ण सरणी की सामग्री है - यह सिर्फ इतना है कि सी में इस मूल्य को पिन करना एक जार में कोहरे को पकड़ने की कोशिश करना है।
कैफे

27

C में, जब आप किसी एक्सप्रेशन में (किसी फ़ंक्शन को पास करने सहित) एक सरणी के नाम का उपयोग करते हैं, जब तक कि यह एड्रेस-इन ( &ऑपरेटर) या sizeofऑपरेटर का ऑपरेटर नहीं है, यह अपने पहले तत्व के लिए एक पॉइंटर का फैसला करता है

यह है कि, अधिकांश संदर्भों में arrayके बराबर है &array[0]दोनों प्रकार और मूल्य में।

आपके उदाहरण में, my_arrayवह प्रकार है char[100]जो char*जब आप इसे प्रिंट करने के लिए पास करते हैं, तो इसका फैसला होता है।

&my_arrayप्रकार char (*)[100](सूचक 100 की सरणी के लिए char) है। जैसा कि यह ऑपरेंड है &, यह उन मामलों में से एक है जो my_arrayतुरंत एक पॉइंटर को अपने पहले तत्व के लिए क्षय नहीं करता है।

सरणी के लिए सूचक के पास सरणी के पहले तत्व के लिए सूचक के रूप में एक ही पता मूल्य है एक सरणी वस्तु के रूप में सरणी तत्वों का सिर्फ एक सन्निहित अनुक्रम है, लेकिन एक सरणी के लिए एक सूचक एक सूचक के लिए एक तत्व के लिए एक अलग प्रकार है वह सरणी। यह महत्वपूर्ण है जब आप सूचक को दो प्रकार के सूचक पर अंकगणित करते हैं।

pointer_to_arrayप्रकार char *- सरणी के पहले तत्व पर इंगित करने के लिए my_arrayआरम्भिक है, जो कि इनिशियल एक्सप्रेशन में निर्धारित होता है - और &pointer_to_array इसमें टाइप char **(पॉइंटर टू ए पॉइंटर टू char) है।

इनमें से: my_array(क्षय के बाद char*), &my_arrayऔर pointer_to_arrayसभी सीधे सरणी या सरणी के पहले तत्व पर इंगित करते हैं और इसलिए समान पता मूल्य होता है।


3

जब आप किसी सरणी के मेमोरी लेआउट को देखते हैं तो उसी पते के कारण my_arrayऔर &my_arrayपरिणाम को आसानी से समझा जा सकता है।

मान लें कि आपके पास 10 वर्णों की एक सरणी है (बजाय आपके कोड में 100)।

char my_array[10];

my_arrayकुछ के लिए स्मृति की तरह लग रहा है:

+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array.

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

printf("my_array = %p\n", my_array);

यदि आप जांचते हैं कि सरणी का पहला तत्व कहाँ है, तो आप देखेंगे कि इसका पता सरणी के पते के समान है:

my_array[0]
|
v
+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array[0].

3

B प्रोग्रामिंग भाषा में, जो C के लिए पूर्ववर्ती था, संकेत और पूर्णांक स्वतंत्र रूप से विनिमेय थे। सिस्टम ऐसा व्यवहार करेगा जैसे कि सभी मेमोरी एक विशाल सरणी थी। प्रत्येक चर नाम के साथ या तो एक वैश्विक या स्टैक-रिलेटिव एड्रेस जुड़ा था, प्रत्येक वैरिएबल नाम के लिए कंपाइलर के पास केवल वही चीजें थीं जिन पर नज़र रखना था, चाहे वह एक ग्लोबल या लोकल वेरिएबल हो, और उसका पता पहले ग्लोबल या लोकल के सापेक्ष हो चर।

जैसे एक वैश्विक घोषणा को देखते हुए i;[एक प्रकार को निर्दिष्ट करने की आवश्यकता नहीं थी, क्योंकि सब कुछ एक पूर्णांक / सूचक था] को संकलक द्वारा संसाधित किया जाएगा: address_of_i = next_global++; memory[address_of_i] = 0;और जैसे एक बयान i++को संसाधित किया जाएगा memory[address_of_i] = memory[address_of_i]+1;:।

जैसे घोषणा की arr[10];प्रक्रिया की जाएगी address_of_arr = next_global; memory[next_global] = next_global; next_global += 10;। ध्यान दें कि जैसे ही उस घोषणा को संसाधित किया गया था, संकलनकर्ता तुरंत arrएक सरणी होने के बारे में भूल सकता है । इस तरह के एक बयान के arr[i]=6;रूप में संसाधित किया जाएगा memory[memory[address_of_a] + memory[address_of_i]] = 6;। कंपाइलर परवाह नहीं करेगा कि क्या arrएक सरणी का प्रतिनिधित्व किया औरi एक पूर्णांक, या इसके विपरीत का । वास्तव में, यह परवाह नहीं करेगा कि वे दोनों सरणियाँ या दोनों पूर्णांक थे; यह पूरी तरह से खुशी से कोड के रूप में वर्णित किया गया है, बिना इस बात की परवाह किए कि परिणामी व्यवहार उपयोगी होगा।

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


1

दरअसल &myarrayऔर myarrayदोनों का आधार पता है।

यदि आप उपयोग करने के बजाय अंतर देखना चाहते हैं

printf("my_array = %p\n", my_array);
printf("my_array = %p\n", &my_array);

उपयोग

printf("my_array = %s\n", my_array);
printf("my_array = %p\n", my_array);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.