किसी सरणी का नाम C में एक सूचक है? यदि नहीं, तो किसी सरणी के नाम और सूचक चर के बीच क्या अंतर है?
&array[0]
एक पॉइंटर की पैदावार करता है, न कि एक सरणी;)
किसी सरणी का नाम C में एक सूचक है? यदि नहीं, तो किसी सरणी के नाम और सूचक चर के बीच क्या अंतर है?
&array[0]
एक पॉइंटर की पैदावार करता है, न कि एक सरणी;)
जवाबों:
एक सरणी एक सरणी है और एक सूचक एक सूचक है, लेकिन ज्यादातर मामलों में सरणी नाम बिंदुओं में परिवर्तित हो जाते हैं । एक शब्द अक्सर इस्तेमाल किया जाता है कि वे संकेत करने के लिए क्षय ।
यहाँ एक सरणी है:
int a[7];
a
इसमें सात पूर्णांक के लिए स्थान होता है, और आप असाइनमेंट के साथ उनमें से किसी एक में मान डाल सकते हैं, जैसे:
a[3] = 9;
यहाँ एक सूचक है:
int *p;
p
पूर्णांक के लिए कोई स्थान नहीं है, लेकिन यह पूर्णांक के लिए एक स्थान को इंगित कर सकता है। उदाहरण के लिए, हम इसे सरणी में किसी एक स्थान पर इंगित करने के लिए सेट कर सकते हैं a
, जैसे कि पहले वाला:
p = &a[0];
क्या भ्रमित हो सकता है कि आप यह भी लिख सकते हैं:
p = a;
यह सरणी की सामग्री को पॉइंटर (जो भी मतलब होगा) में कॉपी नहीं करता है । इसके बजाय, सरणी नाम को उसके पहले तत्व के लिए एक पॉइंटर में बदल दिया जाता है। तो वह असाइनमेंट पिछले वाले की तरह ही होता है।a
p
a
अब आप p
एक सरणी में एक समान तरीके से उपयोग कर सकते हैं :
p[3] = 17;
कारण यह है कि यह काम करता है कि सरणी dereferencing ऑपरेटर सी में, [ ]
संकेत के संदर्भ में परिभाषित किया गया है। x[y]
साधन: सूचक के साथ शुरू x
कदम है, y
के लिए क्या सूचक अंक के बाद तत्वों आगे, और फिर ले जो कुछ भी नहीं है। सूचक अंकगणितीय वाक्यविन्यास का उपयोग करके x[y]
भी लिखा जा सकता है *(x+y)
।
इस तरह के हमारे रूप में एक सामान्य सरणी, के साथ काम करने के लिए a
, नाम a
में a[3]
होगा पहला एक सूचक (में पहला तत्व के लिए परिवर्तित किया जा a
)। फिर हम 3 तत्वों को आगे बढ़ाते हैं, और जो कुछ भी है उसे लेते हैं। दूसरे शब्दों में: सरणी में स्थिति 3 पर तत्व को लें। (जो सरणी में चौथा तत्व है, चूंकि पहला नंबर 0. है।)
तो, सारांश में, सी प्रोग्राम में सरणी के नाम (ज्यादातर मामलों में) पॉइंटर्स में परिवर्तित हो जाते हैं। एक अपवाद तब होता है जब हम sizeof
किसी सरणी पर ऑपरेटर का उपयोग करते हैं । यदि a
इस संदर्भ में एक पॉइंटर में परिवर्तित किया गया था, sizeof a
तो एक पॉइंटर का आकार देगा और वास्तविक सरणी का नहीं, जो कि बेकार होगा, इसलिए उस स्थिति में a
इसका अर्थ है स्वयं सरणी।
functionpointer()
और (*functionpointer)()
एक ही चीज़ का मतलब है, अजीब रूप से पर्याप्त है।
sizeof()
अन्य संदर्भ जिसमें कोई सरणी नहीं है &
- > सूचक क्षय ऑपरेटर है - ऊपर आपके उदाहरण में, &a
7 की सरणी के int
लिए एक सूचक होगा, एक एकल के लिए सूचक नहीं int
; वह है, इसका प्रकार होगा int(*)[7]
, जो कि स्पष्ट रूप से परिवर्तनीय नहीं है int*
। इस तरह, फ़ंक्शंस वास्तव में पॉइंटर्स को विशिष्ट आकार के सरणियों में ले जा सकते हैं, और प्रकार प्रणाली के माध्यम से प्रतिबंध लागू कर सकते हैं।
जब किसी सरणी का उपयोग मान के रूप में किया जाता है, तो उसका नाम पहले तत्व के पते को दर्शाता है।
जब एक सरणी का उपयोग मूल्य के रूप में नहीं किया जाता है तो उसका नाम पूरे सरणी का प्रतिनिधित्व करता है।
int arr[7];
/* arr used as value */
foo(arr);
int x = *(arr + 1); /* same as arr[1] */
/* arr not used as value */
size_t bytes = sizeof arr;
void *q = &arr; /* void pointers are compatible with pointers to any object */
यदि सरणी प्रकार की अभिव्यक्ति (जैसे कि सरणी का नाम) एक बड़ी अभिव्यक्ति में दिखाई देती है और यह &
या तो या sizeof
ऑपरेटरों का संचालक नहीं है, तो सरणी अभिव्यक्ति का प्रकार "एन-तत्व सरणी ऑफ़ टी" से परिवर्तित हो जाता है। "टी के लिए पॉइंटर", और अभिव्यक्ति का मूल्य सरणी में पहले तत्व का पता है।
संक्षेप में, सरणी नाम एक सूचक नहीं है, लेकिन अधिकांश संदर्भों में यह माना जाता है कि यह एक सूचक था।
संपादित करें
टिप्पणी में सवाल का जवाब दे:
यदि मैं आकार का उपयोग करता हूं, तो क्या मैं सरणी के केवल तत्वों के आकार को गिनता हूं? फिर सरणी "हेड" भी लंबाई और एक पॉइंटर के बारे में जानकारी के साथ जगह लेता है (और इसका मतलब है कि यह सामान्य पॉइंटर की तुलना में अधिक जगह लेता है)?
जब आप एक सरणी बनाते हैं, तो केवल वही स्थान आवंटित किया जाता है जो स्वयं तत्वों के लिए स्थान है; अलग-अलग पॉइंटर या किसी मेटाडेटा के लिए कोई भंडारण नहीं किया जाता है। दिया हुआ
char a[10];
आपको याद में क्या मिलता है
+---+
a: | | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
...
+---+
| | a[9]
+---+
अभिव्यक्ति a
पूरे सरणी को संदर्भित करता है, लेकिन कोई है वस्तु a
सरणी तत्व खुद को अलग से। इस प्रकार, sizeof a
आपको पूरे सरणी का आकार (बाइट्स में) देता है। अभिव्यक्ति &a
आपको सरणी का पता देती है , जो पहले तत्व के पते के समान है । बीच का अंतर &a
और &a[0]
परिणाम 1 का प्रकार है - char (*)[10]
पहले मामले char *
में और दूसरे में।
जहां चीजें अजीब होती हैं, जब आप व्यक्तिगत तत्वों का उपयोग करना चाहते हैं - तो अभिव्यक्ति a[i]
को उसी के परिणाम के रूप में परिभाषित किया जाता है *(a + i)
- उस पते से a
ऑफसेट i
तत्व ( बाइट्स नहीं ) और परिणाम को निष्क्रिय करने पर।
समस्या यह है कि a
सूचक या पता नहीं है - यह संपूर्ण सरणी ऑब्जेक्ट है। इस प्रकार, C में नियम यह है कि जब भी कंपाइलर सरणी प्रकार (जैसे कि a
, जिसमें टाइप होता है char [10]
) की अभिव्यक्ति देखता है और वह एक्सप्रेशन sizeof
या unary &
ऑपरेटर्स का ऑपरेंड नहीं होता है , तो उस एक्सप्रेशन का प्रकार परिवर्तित हो जाता है ("decays") एक सूचक प्रकार ( char *
), और अभिव्यक्ति का मान सरणी के पहले तत्व का पता है। इसलिए, अभिव्यक्ति a
का अभिव्यक्ति के रूप में एक ही प्रकार और मूल्य है &a[0]
(और विस्तार से, अभिव्यक्ति *a
का अभिव्यक्ति के समान और मूल्य है a[0]
)।
सी एक पहले बी कहा जाता भाषा से लिया गया है, और बी में a
था सरणी तत्वों से एक अलग सूचक वस्तु a[0]
, a[1]
आदि रिची बी की सरणी अर्थ विज्ञान रखना चाहता है, लेकिन वह अलग सूचक वस्तु भंडारण के साथ गड़बड़ नहीं करना चाहता था। इसलिए उसने इससे छुटकारा पा लिया। इसके बजाय, संकलक अनुवाद के दौरान सरणी के भावों को सूचक भावों में बदल देगा।
याद रखें कि मैंने कहा कि सरणियाँ उनके आकार के बारे में कोई मेटाडेटा संग्रहीत नहीं करती हैं। जैसे ही उस एक्सप्रेशन एक्सप्रेशन एक पॉइंटर को "डिसकस" करता है, आपके पास एक ही एलिमेंट का पॉइंटर होता है। वह तत्व तत्वों के अनुक्रम का पहला हो सकता है, या यह एक एकल वस्तु हो सकता है। सूचक के आधार पर पता करने का कोई तरीका नहीं है।
जब आप किसी फ़ंक्शन के लिए एक सरणी अभिव्यक्ति पास करते हैं, तो प्राप्त होने वाला सभी फ़ंक्शन पहले तत्व के लिए एक संकेतक है - यह पता नहीं है कि सरणी कितनी बड़ी है (यही कारण है कि gets
फ़ंक्शन इस तरह का एक खतरा था और अंततः पुस्तकालय से हटा दिया गया था)। फ़ंक्शन के पास यह जानने के लिए कि सरणी में कितने तत्व हैं, आपको या तो एक सेंटिनल मान (जैसे कि C स्ट्रिंग्स में 0 टर्मिनेटर) का उपयोग करना होगा या आपको एक अलग पैरामीटर के रूप में तत्वों की संख्या को पास करना होगा।
sizeof
एक ऑपरेटर है, और यह ऑपरेंड में संख्या बाइट्स का मूल्यांकन करता है (या तो किसी ऑब्जेक्ट को दर्शाती एक अभिव्यक्ति, या कोष्ठक में एक प्रकार का नाम)। तो, एक सरणी के लिए, sizeof
किसी एकल तत्व में बाइट्स की संख्या से गुणा किए गए तत्वों की संख्या का मूल्यांकन करता है। यदि एक int
4 बाइट्स चौड़ा है, तो एक 5-तत्व सरणी int
20 बाइट्स लेती है।
[ ]
भी विशेष नहीं है? उदाहरण के लिए, int a[2][3];
फिर x = a[1][2];
, हालांकि, इसे फिर से लिखा जा सकता है x = *( *(a+1) + 2 );
, यहाँ a
एक पॉइंटर प्रकार में परिवर्तित नहीं किया जाता है int*
(हालांकि यदि a
किसी फ़ंक्शन का तर्क है तो इसे परिवर्तित किया जाना चाहिए int*
)।
a
का प्रकार है int [2][3]
, जो टाइप करने के लिए " डिकेस " करता है int (*)[3]
। अभिव्यक्ति *(a + 1)
के प्रकार हैं int [3]
, जो "करने के लिए" का फैसला करता है int *
। इस प्रकार, प्रकार *(*(a + 1) + 2)
होगा int
। a
के पहले 3-तत्व वाली सरणी के लिए अंक int
, a + 1
के दूसरे 3-तत्व सरणी के लिए अंक int
, *(a + 1)
है की दूसरी 3-तत्व सरणी int
, *(a + 1) + 2
की दूसरी सरणी के तीसरे तत्व को अंक int
है, इसलिए *(*(a + 1) + 2)
है की दूसरी सरणी के तीसरे तत्व int
। कैसे मशीन कोड के लिए मैप किया जाता है पूरी तरह से कंपाइलर तक।
इस तरह घोषित एक सरणी
int a[10];
10 int
एस के लिए स्मृति आवंटित करता है । आप संशोधित a
नहीं कर सकते, लेकिन आप सूचक अंकगणित के साथ कर सकते हैं a
।
इस तरह एक पॉइंटर सिर्फ पॉइंटर के लिए मेमोरी आवंटित करता है p
:
int *p;
यह किसी भी int
एस को आवंटित नहीं करता है । आप इसे संशोधित कर सकते हैं:
p = a;
और सरणी सदस्यता का उपयोग कर सकते हैं:
p[2] = 5;
a[2] = 5; // same
*(p+2) = 5; // same effect
*(a+2) = 5; // same effect
int
स्वचालित भंडारण अवधि के साथ 10 एस आवंटित करें ' ।
सरणी नाम अपने आप में एक स्मृति स्थान देता है, इसलिए आप एक सूचक की तरह सरणी नाम का इलाज कर सकते हैं:
int a[7];
a[0] = 1976;
a[1] = 1984;
printf("memory location of a: %p", a);
printf("value at memory location %p is %d", a, *a);
और अन्य निफ्टी सामान आप सूचक को कर सकते हैं (जैसे एक ऑफसेट जोड़ना / घटाना), आप एक सरणी में भी कर सकते हैं:
printf("value at memory location %p is %d", a + 1, *(a + 1));
भाषा-वार, यदि C ने सरणी को "पॉइंटर" के कुछ प्रकार के रूप में उजागर नहीं किया है (वस्तुतः यह केवल एक स्मृति स्थान है। यह मेमोरी में मनमाने स्थान को इंगित नहीं कर सकता है, और न ही प्रोग्रामर द्वारा नियंत्रित किया जा सकता है)। हमें हमेशा इसे कोड करने की आवश्यकता है:
printf("value at memory location %p is %d", &a[1], a[1]);
मुझे लगता है कि यह उदाहरण इस मुद्दे पर कुछ प्रकाश डालता है:
#include <stdio.h>
int main()
{
int a[3] = {9, 10, 11};
int **b = &a;
printf("a == &a: %d\n", a == b);
return 0;
}
यह 4.9.2 में ठीक (2 चेतावनियों के साथ) संकलन करता है, और निम्नलिखित प्रिंट करता है:
a == &a: 1
उफ़ :-)
तो, निष्कर्ष नहीं है, सरणी एक सूचक नहीं है, यह एक सूचक के रूप में मेमोरी में (यहां तक कि केवल-पढ़ने के लिए नहीं) संग्रहीत नहीं है, भले ही ऐसा दिखता है, क्योंकि आप & ऑपरेटर के साथ अपना पता प्राप्त कर सकते हैं । लेकिन - उफ़ - वह ऑपरेटर काम नहीं करता है :-)), किसी भी तरह, आपको चेतावनी दी गई है:
p.c: In function ‘main’:
pp.c:6:12: warning: initialization from incompatible pointer type
int **b = &a;
^
p.c:8:28: warning: comparison of distinct pointer types lacks a cast
printf("a == &a: %d\n", a == b);
C ++ संकलन-समय में त्रुटियों के साथ ऐसे किसी भी प्रयास से इनकार करता है।
संपादित करें:
यह वही है जो मुझे प्रदर्शित करने का मतलब है:
#include <stdio.h>
int main()
{
int a[3] = {9, 10, 11};
void *c = a;
void *b = &a;
void *d = &c;
printf("a == &a: %d\n", a == b);
printf("c == &c: %d\n", c == d);
return 0;
}
भले ही c
और a
उसी मेमोरी में "पॉइंट" हो, आप c
पॉइंटर का एड्रेस प्राप्त कर सकते हैं , लेकिन आप पॉइंटर का एड्रेस प्राप्त नहीं कर सकते a
।
-std=c11 -pedantic-errors
, तो आपको अमान्य C कोड लिखने के लिए एक कंपाइलर त्रुटि मिलती है। इसका कारण यह है क्योंकि आप int (*)[3]
एक वैरिएबल को असाइन करने का प्रयास करते हैं int**
, जो दो प्रकार के होते हैं जिनका एक दूसरे से कोई लेना-देना नहीं है। तो यह उदाहरण क्या साबित करने वाला है, मुझे पता नहीं है।
int **
प्रकार बिंदु वहाँ नहीं है, एक बेहतर इस्तेमाल करना चाहिए void *
इस के लिए।
सरणी नाम सूचक की तरह व्यवहार करता है और सरणी के पहले तत्व को इंगित करता है। उदाहरण:
int a[]={1,2,3};
printf("%p\n",a); //result is similar to 0x7fff6fe40bc0
printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0
दोनों प्रिंट स्टेटमेंट मशीन के लिए बिल्कुल समान आउटपुट देंगे। मेरे सिस्टम में यह दिया गया है:
0x7fff6fe40bc0
एक सरणी स्मृति में एकांत और सन्निहित तत्वों का एक संग्रह है। सी में एक सरणी का नाम पहले तत्व का सूचकांक है, और एक ऑफसेट को लागू करने से आप बाकी तत्वों तक पहुंच सकते हैं। एक "पहले तत्व का सूचकांक" वास्तव में एक स्मृति दिशा का सूचक है।
पॉइंटर वेरिएबल्स के साथ अंतर यह है कि आप उस स्थान को नहीं बदल सकते हैं जिस पर सरणी का नाम इंगित कर रहा है, इसलिए एक कॉन्स्टेंट पॉइंटर के समान है (यह समान है, समान नहीं है। मार्क की टिप्पणी देखें)। लेकिन यह भी कि अगर आपको सूचक अंकगणितीय का उपयोग करने के लिए मान प्राप्त करने के लिए सरणी नाम की आवश्यकता नहीं है:
char array = "hello wordl";
char* ptr = array;
char c = array[2]; //array[2] holds the character 'l'
char *c1 = ptr[2]; //ptr[2] holds a memory direction that holds the character 'l'
तो इसका जवाब थोथा है 'हां'।
सरणी नाम सरणी के 1 तत्व का पता है। तो हाँ सरणी नाम एक कॉन्स्टेंट पॉइंटर है।