एक सरणी नाम एक सूचक है?


203

किसी सरणी का नाम C में एक सूचक है? यदि नहीं, तो किसी सरणी के नाम और सूचक चर के बीच क्या अंतर है?


4
नहीं, लेकिन सरणी समान है और सरणी [0]

36
@ पीएसटी: &array[0]एक पॉइंटर की पैदावार करता है, न कि एक सरणी;)
जल्फ

28
@Nava (और pst): सरणी और सरणी [0] वास्तव में समान नहीं हैं। बिंदु में मामला: sizeof (सरणी) और sizeof (और सरणी [0]) अलग-अलग परिणाम देते हैं।
थॉमस पैड्रॉन-मैक्कार्थी

1
@ थोमस सहमत हैं, लेकिन संकेत के संदर्भ में, जब आप सरणी और सरणी [0] को छोड़ते हैं, तो वे सरणी [0] .ie * सरणी == सरणी [0] का समान मान उत्पन्न करते हैं। किसी का मतलब यह नहीं था कि ये दोनों संकेत समान हैं, लेकिन इस विशिष्ट मामले में (पहले तत्व की ओर इशारा करते हुए) आप सरणी के नाम का भी उपयोग कर सकते हैं।
नवा कार्मोन

1
ये आपकी समझ में भी मदद कर सकते हैं: stackoverflow.com/questions/381542 , stackoverflow.com/questions/660752
दीना

जवाबों:


255

एक सरणी एक सरणी है और एक सूचक एक सूचक है, लेकिन ज्यादातर मामलों में सरणी नाम बिंदुओं में परिवर्तित हो जाते हैं । एक शब्द अक्सर इस्तेमाल किया जाता है कि वे संकेत करने के लिए क्षय

यहाँ एक सरणी है:

int a[7];

a इसमें सात पूर्णांक के लिए स्थान होता है, और आप असाइनमेंट के साथ उनमें से किसी एक में मान डाल सकते हैं, जैसे:

a[3] = 9;

यहाँ एक सूचक है:

int *p;

pपूर्णांक के लिए कोई स्थान नहीं है, लेकिन यह पूर्णांक के लिए एक स्थान को इंगित कर सकता है। उदाहरण के लिए, हम इसे सरणी में किसी एक स्थान पर इंगित करने के लिए सेट कर सकते हैं a, जैसे कि पहले वाला:

p = &a[0];

क्या भ्रमित हो सकता है कि आप यह भी लिख सकते हैं:

p = a;

यह सरणी की सामग्री को पॉइंटर (जो भी मतलब होगा) में कॉपी नहीं करता है । इसके बजाय, सरणी नाम को उसके पहले तत्व के लिए एक पॉइंटर में बदल दिया जाता है। तो वह असाइनमेंट पिछले वाले की तरह ही होता है।apa

अब आप 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इसका अर्थ है स्वयं सरणी।


5
एक समान स्वचालित रूपांतरण फ़ंक्शन पॉइंटर्स पर लागू होता है - दोनों functionpointer()और (*functionpointer)()एक ही चीज़ का मतलब है, अजीब रूप से पर्याप्त है।
कार्ल नॉरम

3
उन्होंने यह नहीं पूछा कि क्या सरणियां और संकेत समान हैं, लेकिन अगर किसी सरणी का नाम एक सूचक है
रिकार्डो एमोरेस

32
एक सरणी नाम सूचक नहीं है। यह प्रकार सरणी के एक चर के लिए एक पहचानकर्ता है, जिसमें तत्व प्रकार के सूचक के लिए एक अंतर्निहित रूपांतरण है।
पावेल मिनाएव

29
इसके अलावा, इसके अलावा, sizeof()अन्य संदर्भ जिसमें कोई सरणी नहीं है &- > सूचक क्षय ऑपरेटर है - ऊपर आपके उदाहरण में, &a7 की सरणी के intलिए एक सूचक होगा, एक एकल के लिए सूचक नहीं int; वह है, इसका प्रकार होगा int(*)[7], जो कि स्पष्ट रूप से परिवर्तनीय नहीं है int*। इस तरह, फ़ंक्शंस वास्तव में पॉइंटर्स को विशिष्ट आकार के सरणियों में ले जा सकते हैं, और प्रकार प्रणाली के माध्यम से प्रतिबंध लागू कर सकते हैं।
पावेल मिनाएव

3
@ onmyway133, जाँच यहाँ एक संक्षिप्त विवरण और आगे प्रशंसा पत्र के लिए।
कार्ल नॉरम

36

जब किसी सरणी का उपयोग मान के रूप में किया जाता है, तो उसका नाम पहले तत्व के पते को दर्शाता है।
जब एक सरणी का उपयोग मूल्य के रूप में नहीं किया जाता है तो उसका नाम पूरे सरणी का प्रतिनिधित्व करता है।

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 */

20

यदि सरणी प्रकार की अभिव्यक्ति (जैसे कि सरणी का नाम) एक बड़ी अभिव्यक्ति में दिखाई देती है और यह &या तो या 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 टर्मिनेटर) का उपयोग करना होगा या आपको एक अलग पैरामीटर के रूप में तत्वों की संख्या को पास करना होगा।


  1. पता मूल्य की व्याख्या किस तरह से * प्रभावित हो सकती है - मशीन पर निर्भर करता है।

इस उत्तर के लिए काफी लंबे समय से देख रहे हैं। धन्यवाद! और यदि आप जानते हैं, तो क्या आप आगे बता सकते हैं कि एक सरणी अभिव्यक्ति क्या है। अगर मैं आकार का उपयोग करता हूं, तो क्या मैं केवल सरणी के तत्वों के आकार को गिनता हूं? फिर सरणी "हेड" भी लंबाई और एक पॉइंटर के बारे में जानकारी के साथ जगह लेता है (और इसका मतलब है कि यह सामान्य पॉइंटर की तुलना में अधिक जगह लेता है)?
एंड्री डेम्यट्रुक

एक और बात। लंबाई 5 की एक सरणी int int [5] है। तो वह वह जगह है जहां से हम लंबाई जानते हैं जब हम आकार (सरणी) कहते हैं - इसके प्रकार से? और इसका मतलब है कि विभिन्न लंबाई के सरणियाँ विभिन्न प्रकार के स्थिरांक की तरह हैं?
एंड्री डेमट्रुक

@AndriyDmytruk: sizeofएक ऑपरेटर है, और यह ऑपरेंड में संख्या बाइट्स का मूल्यांकन करता है (या तो किसी ऑब्जेक्ट को दर्शाती एक अभिव्यक्ति, या कोष्ठक में एक प्रकार का नाम)। तो, एक सरणी के लिए, sizeofकिसी एकल तत्व में बाइट्स की संख्या से गुणा किए गए तत्वों की संख्या का मूल्यांकन करता है। यदि एक int4 बाइट्स चौड़ा है, तो एक 5-तत्व सरणी int20 बाइट्स लेती है।
जॉन बोडे

ऑपरेटर [ ]भी विशेष नहीं है? उदाहरण के लिए, int a[2][3];फिर x = a[1][2];, हालांकि, इसे फिर से लिखा जा सकता है x = *( *(a+1) + 2 );, यहाँ aएक पॉइंटर प्रकार में परिवर्तित नहीं किया जाता है int*(हालांकि यदि aकिसी फ़ंक्शन का तर्क है तो इसे परिवर्तित किया जाना चाहिए int*)।
स्टेन

2
@Stan: अभिव्यक्ति aका प्रकार है int [2][3], जो टाइप करने के लिए " डिकेस " करता है int (*)[3]। अभिव्यक्ति *(a + 1)के प्रकार हैं int [3], जो "करने के लिए" का फैसला करता है int *। इस प्रकार, प्रकार *(*(a + 1) + 2)होगा intaके पहले 3-तत्व वाली सरणी के लिए अंक int, a + 1के दूसरे 3-तत्व सरणी के लिए अंक int, *(a + 1) है की दूसरी 3-तत्व सरणी int, *(a + 1) + 2की दूसरी सरणी के तीसरे तत्व को अंक intहै, इसलिए *(*(a + 1) + 2) है की दूसरी सरणी के तीसरे तत्व int। कैसे मशीन कोड के लिए मैप किया जाता है पूरी तरह से कंपाइलर तक।
जॉन बोड

5

इस तरह घोषित एक सरणी

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

2
स्टैक पर हमेशा ऐरे आवंटित नहीं किए जाते हैं। यहत का कार्यान्वयन विवरण जो संकलक से संकलक तक अलग-अलग होगा। ज्यादातर मामलों में स्थिर या वैश्विक सरणियों को स्टैक की तुलना में एक अलग मेमोरी क्षेत्र से आवंटित किया जाएगा। कास्ट प्रकार के एरे को स्मृति के एक और क्षेत्र से आवंटित किया जा सकता है
मार्क बेसी

1
मुझे लगता है कि ग्रुमड्रिग कहने का मतलब है " intस्वचालित भंडारण अवधि के साथ 10 एस आवंटित करें ' ।
ऑर्बिट में लाइटनेस दौड़

4

सरणी नाम अपने आप में एक स्मृति स्थान देता है, इसलिए आप एक सूचक की तरह सरणी नाम का इलाज कर सकते हैं:

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]);

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


1
"यह ठीक संकलित करता है (2 चेतावनियों के साथ)"। यह ठीक नहीं है। यदि आप इसे जोड़कर उचित मानक C के रूप में संकलित करने के लिए gcc से कहते हैं -std=c11 -pedantic-errors, तो आपको अमान्य C कोड लिखने के लिए एक कंपाइलर त्रुटि मिलती है। इसका कारण यह है क्योंकि आप int (*)[3]एक वैरिएबल को असाइन करने का प्रयास करते हैं int**, जो दो प्रकार के होते हैं जिनका एक दूसरे से कोई लेना-देना नहीं है। तो यह उदाहरण क्या साबित करने वाला है, मुझे पता नहीं है।
लुंडिन

आपकी टिप्पणी के लिए धन्यवाद लंडिन। आप जानते हैं कि कई मानक हैं। मैंने यह स्पष्ट करने का प्रयास किया कि संपादन में मेरा क्या मतलब है। int **प्रकार बिंदु वहाँ नहीं है, एक बेहतर इस्तेमाल करना चाहिए void *इस के लिए।
पालो

-3

सरणी नाम सूचक की तरह व्यवहार करता है और सरणी के पहले तत्व को इंगित करता है। उदाहरण:

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

-4

एक सरणी स्मृति में एकांत और सन्निहित तत्वों का एक संग्रह है। सी में एक सरणी का नाम पहले तत्व का सूचकांक है, और एक ऑफसेट को लागू करने से आप बाकी तत्वों तक पहुंच सकते हैं। एक "पहले तत्व का सूचकांक" वास्तव में एक स्मृति दिशा का सूचक है।

पॉइंटर वेरिएबल्स के साथ अंतर यह है कि आप उस स्थान को नहीं बदल सकते हैं जिस पर सरणी का नाम इंगित कर रहा है, इसलिए एक कॉन्स्टेंट पॉइंटर के समान है (यह समान है, समान नहीं है। मार्क की टिप्पणी देखें)। लेकिन यह भी कि अगर आपको सूचक अंकगणितीय का उपयोग करने के लिए मान प्राप्त करने के लिए सरणी नाम की आवश्यकता नहीं है:

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
एक सरणी नाम एक कॉन्स्टेंट पॉइंटर के समान नहीं है। दिया गया: int [10]; int * p = a; sizeof (p) और sizeof (a) समान नहीं हैं।
मार्क बेसी

1
अन्य अंतर हैं। सामान्य तौर पर, सी मानक द्वारा उपयोग की जाने वाली शब्दावली से चिपकना सबसे अच्छा है, जो विशेष रूप से इसे "रूपांतरण" कहता है। उद्धरण: "सिवाय इसके कि जब यह आकार ऑपरेटर या यूनिरी और ऑपरेटर का ऑपरेंड है, या किसी सरणी को इनिशियलाइज़ करने के लिए उपयोग किया जाने वाला एक स्ट्रिंग शाब्दिक है, तो एक अभिव्यक्ति जिसमें 'टाइप का प्रकार' होता है, को टाइप के साथ एक एक्सप्रेशन में बदल दिया जाता है" 'पॉइंटर टाइप करने के लिए' जो एरे ऑब्जेक्ट के प्रारंभिक तत्व को इंगित करता है और एक अंतराल नहीं है। यदि एरे ऑब्जेक्ट में स्टोरेज क्लास रजिस्टर है, तो व्यवहार अपरिभाषित है। "
पावेल मिनाएव

-5

सरणी नाम सरणी के 1 तत्व का पता है। तो हाँ सरणी नाम एक कॉन्स्टेंट पॉइंटर है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.