C में char array और char pointer में क्या अंतर है?


216

मैं सी में संकेत समझने की कोशिश कर रहा हूं, लेकिन मैं वर्तमान में निम्नलिखित के साथ भ्रमित हूं:

  • char *p = "hello"

    यह एक वर्ण सूचक है जो वर्ण सरणी पर इंगित करता है, h से शुरू होता है ।

  • char p[] = "hello"

    यह एक सरणी है जो हैलो को स्टोर करती है ।

जब मैं इस फ़ंक्शन में इन दोनों चर को पास करता हूं तो क्या अंतर है?

void printSomething(char *p)
{
    printf("p: %s",p);
}

5
यह मान्य नहीं होगा: char p[3] = "hello";आपके द्वारा घोषित ऐरे के आकार के लिए इनिशलाइज़र स्ट्रिंग बहुत लंबा है। लेखन त्रुटि है?
कोड़ी ग्रे

16
या बस char p[]="hello";पर्याप्त होगा!
गहरी


1
संभव डुप्लिकेट क्या चार एस और] के बीच अंतर है? सच है, यह विशेष रूप से फ़ंक्शन पैरामीटर के बारे में पूछता है, लेकिन यह charविशिष्ट नहीं है ।
सिरो सांटिल्ली 44 iro i i

1
आपको यह समझने की आवश्यकता है कि वे मौलिक रूप से भिन्न हैं। इसमें एकमात्र समानता यह है कि arry p [] एक आधार सूचक है जो एक सूचक के माध्यम से array p [] तक पहुंचने में सक्षम है। p [] अपने आप में एक स्ट्रिंग के लिए मेमोरी रखता है, जबकि * p सिर्फ वन CHAR के पहले तत्व (यानी, पहले से आवंटित स्ट्रिंग के आधार को इंगित करता है) को संबोधित करता है। इसे बेहतर ढंग से समझाने के लिए, नीचे विचार करें: char * cPtr = {'h', 'e', ​​'l', 'l', 'o', '\ 0'}; ==> यह एक त्रुटि है, क्योंकि cPtr केवल एक वर्ण चार cBuff [] = {'h', 'e', ​​'l', 'l', 'o', '\ 0'} का सूचक है; ==> यह ठीक है, है bcos cBuff अपने आप में एक चार सरणी है
Ilavarasan

जवाबों:


223

char*और char[] विभिन्न प्रकार हैं , लेकिन यह सभी मामलों में तुरंत स्पष्ट नहीं है। ऐसा इसलिए है क्योंकि सरणियों को संकेत में क्षय होता है , जिसका अर्थ है कि यदि प्रकार की अभिव्यक्ति char[]प्रदान की जाती है जहां एक प्रकार char*की उम्मीद की जाती है, तो संकलक स्वचालित रूप से सरणी को इसके पहले तत्व में परिवर्तित करता है।

आपका उदाहरण फ़ंक्शन printSomethingएक पॉइंटर की अपेक्षा करता है, इसलिए यदि आप इस तरह से एक सरणी पास करने की कोशिश करते हैं:

char s[10] = "hello";
printSomething(s);

संकलक का दावा है कि आपने यह लिखा था:

char s[10] = "hello";
printSomething(&s[0]);

क्या 2012 से अब तक कुछ बदला है। एक चरित्र सरणी "s" के लिए पूरे सरणी को प्रिंट करता है .. अर्थात, "हैलो"
भानु तेज

@BhanuTez नहीं, डेटा को कैसे संग्रहीत किया जाता है और डेटा के साथ क्या किया जाता है, यह अलग चिंताएं हैं। यह उदाहरण पूरे स्ट्रिंग को प्रिंट करता है क्योंकि printfइस तरह से %sप्रारूप स्ट्रिंग को संभालता है : प्रदान किए गए पते पर शुरू करें और शून्य टर्मिनेटर का सामना करने तक जारी रखें। यदि आप %cउदाहरण के लिए, प्रारूप स्ट्रिंग का उपयोग कर सकते हैं, तो आप केवल एक वर्ण प्रिंट करना चाहते हैं ।
IX3

बस यह पूछना चाहता था कि क्या चार [] सरणी के मामले में char *p = "abc";NULL वर्ण \0स्वचालित रूप से जोड़ा जाता है?
केपीएमजी

मैं क्यों सेट char *name; name="123";कर सकता हूं लेकिन intटाइप के साथ भी ऐसा कर सकता हूं ? और %cप्रिंट करने के लिए उपयोग करने के बाद name, आउटपुट अपठनीय स्ट्रिंग है :?
टॉमस्वर

83

चलो देखते हैं:

#include <stdio.h>
#include <string.h>

int main()
{
    char *p = "hello";
    char q[] = "hello"; // no need to count this

    printf("%zu\n", sizeof(p)); // => size of pointer to char -- 4 on x86, 8 on x86-64
    printf("%zu\n", sizeof(q)); // => size of char array in memory -- 6 on both

    // size_t strlen(const char *s) and we don't get any warnings here:
    printf("%zu\n", strlen(p)); // => 5
    printf("%zu\n", strlen(q)); // => 5

    return 0;
}

foo * और foo [] अलग-अलग प्रकार के होते हैं और उन्हें कंपाइलर (पॉइंटर = एड्रेस + पॉइंटर के प्रकार, सरणी = पॉइंटर + ऐरे की वैकल्पिक लंबाई) के द्वारा अलग-अलग हैंडल किया जाता है, यदि ज्ञात हो, उदाहरण के लिए, यदि सरणी स्टेटिक रूप से आबंटित है ), विवरण मानक में पाया जा सकता है। और रनटाइम के स्तर पर उनके बीच कोई अंतर नहीं है (कोडांतरक में, अच्छी तरह से, लगभग, नीचे देखें)।

साथ ही, सी एफएक्यू में एक संबंधित प्रश्न है :

प्रश्न : इन इनिशियलाइज़ेशन में क्या अंतर है?

char a[] = "string literal";   
char *p  = "string literal";   

यदि मैं p [i] के लिए एक नया मान निर्दिष्ट करने का प्रयास करता हूं तो मेरा प्रोग्राम क्रैश हो जाता है।

एक : एक स्ट्रिंग शाब्दिक (सी स्रोत में एक डबल-उद्धृत स्ट्रिंग के लिए औपचारिक शब्द) दो अलग-अलग तरीकों से उपयोग किया जा सकता है:

  1. एक सरणी के लिए आरंभीकरणकर्ता के रूप में, चार [] की घोषणा के रूप में, यह उस सरणी में वर्णों के प्रारंभिक मूल्यों को निर्दिष्ट करता है (और, यदि आवश्यक हो, तो इसका आकार)।
  2. कहीं भी, यह वर्णों के एक अनाम, स्थिर सरणी में बदल जाता है, और यह अनाम सरणी केवल-पढ़ने के लिए स्मृति में संग्रहीत किया जा सकता है, और इसलिए जरूरी नहीं कि इसे संशोधित किया जा सके। एक अभिव्यक्ति के संदर्भ में, सरणी को एक बार एक पॉइंटर में बदल दिया जाता है, हमेशा की तरह (खंड 6 देखें), इसलिए दूसरी घोषणा पी को प्रारंभिक नाम के पहले तत्व की ओर इंगित करती है।

कुछ कंपाइलरों में एक स्विच होता है जो नियंत्रित करता है कि स्ट्रिंग शाब्दिक हैं या नहीं (पुराने कोड को संकलित करने के लिए), और कुछ के पास विकल्प हो सकते हैं कि स्ट्रिंग शाब्दिकों को औपचारिक रूप से कांस्ट चार्ज (बेहतर त्रुटि पकड़ने के लिए) के सरणियों के रूप में माना जाए।

1.31, 6.1, 6.2, 6.8 और 11.8b पर भी प्रश्न देखें।

संदर्भ: K & R2 Sec। 5.5 पी। 104

आईएसओ सेक। 6.1.4, सेक। 6.5.7

राशनले सेक। 3.1.4

एच एंड एस सेक। 2.7.4 पीपी। 31-2


साइज़ोफ़ (q) में, क्यू एक सूचक में क्षय नहीं करता, जैसा कि @Jon ने अपने उत्तर में उल्लेख किया है?
गारीप

@garyp q एक पॉइंटर में क्षय नहीं करता है क्योंकि साइज़ोफ़ एक ऑपरेटर है, फ़ंक्शन नहीं है (भले ही साइज़ोफ़ एक फ़ंक्शन था, क्यू केवल तभी क्षय होगा यदि फ़ंक्शन एक चार्ट पॉइंटर की उम्मीद कर रहा था)।
गिरीबी

धन्यवाद, लेकिन प्रिंटफ ("% u \ n" के बजाय प्रिंटफ ("% zu \ n"), मुझे लगता है कि z को हटा देना चाहिए।
ज़कारिया

33

C में char array बनाम char pointer में क्या अंतर है?

C99 N1256 ड्राफ्ट

चरित्र स्ट्रिंग शाब्दिक के दो अलग-अलग उपयोग हैं:

  1. आरंभ करें char[]:

    char c[] = "abc";      

    यह "अधिक जादू" है, और 6.7.8 / 14 "प्रारंभिककरण" में वर्णित है:

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

    तो यह सिर्फ एक शॉर्टकट है:

    char c[] = {'a', 'b', 'c', '\0'};

    किसी भी अन्य नियमित सरणी की तरह, cसंशोधित किया जा सकता है।

  2. हर जगह: यह एक उत्पन्न करता है:

    इसलिए जब आप लिखते हैं:

    char *c = "abc";

    यह निम्न के समान है:

    /* __unnamed is magic because modifying it gives UB. */
    static char __unnamed[] = "abc";
    char *c = __unnamed;

    नोट से निहित कलाकारों char[]के लिए char *है, जो हमेशा कानूनी है।

    फिर यदि आप संशोधित करते हैं c[0], तो आप संशोधित भी करते हैं __unnamed, जो कि यूबी है।

    यह 6.4.5 "स्ट्रिंग शाब्दिक" पर प्रलेखित है:

    5 अनुवाद चरण 7 में, एक बाइट या मूल्य शून्य का कोड प्रत्येक मल्टीबीट चरित्र अनुक्रम में जोड़ा जाता है जो एक स्ट्रिंग शाब्दिक या शाब्दिक परिणाम होता है। मल्टीबाइट कैरेक्टर सीक्वेंस का उपयोग तब स्टैटिक स्टोरेज की अवधि और लंबाई को समाहित करने के लिए किया जाता है। चरित्र स्ट्रिंग शाब्दिकों के लिए, सरणी तत्वों में चार वर्ण होते हैं, और इन्हें मल्टीबाइट वर्ण अनुक्रम के अलग-अलग बाइट्स के साथ आरंभ किया जाता है ...]

    6 यह स्पष्ट नहीं है कि क्या ये सरणियाँ अलग-अलग हैं बशर्ते उनके तत्वों में उचित मूल्य हों। यदि प्रोग्राम ऐसे सरणी को संशोधित करने का प्रयास करता है, तो व्यवहार अपरिभाषित है।

6.7.8 / 32 "प्रारंभिक" एक प्रत्यक्ष उदाहरण देता है:

उदाहरण 8: घोषणा

char s[] = "abc", t[3] = "abc";

"सादे" वर्ण सरणी वस्तुओं को परिभाषित करता है sऔर tजिनके तत्व वर्ण स्ट्रिंग शाब्दिक के साथ आरंभिक होते हैं।

यह घोषणा समान है

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

सरणियों की सामग्री परिवर्तनीय हैं। दूसरी ओर, घोषणा

char *p = "abc";

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

जीसीसी 4.8 x86-64 ईएलएफ कार्यान्वयन

कार्यक्रम:

#include <stdio.h>

int main(void) {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

संकलन और विघटित:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

आउटपुट में शामिल हैं:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

निष्कर्ष: GCC char*इसे .rodataअनुभाग में संग्रहीत करता है , अंदर नहीं .text

यदि हम ऐसा ही करते हैं char[]:

 char s[] = "abc";

हमने प्राप्त किया:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

तो यह स्टैक में संग्रहीत हो जाता है (सापेक्ष %rbp)।

हालांकि ध्यान दें कि डिफ़ॉल्ट लिंकर स्क्रिप्ट डालता है .rodataऔर .textउसी सेगमेंट में होता है, जिसने निष्पादित किया है, लेकिन कोई लिखित अनुमति नहीं है। इसके साथ मनाया जा सकता है:

readelf -l a.out

जिसमें है:

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata

2
@ leszek.hanusz अपरिभाषित व्यवहार stackoverflow.com/questions/2766731/… Google "C भाषा UB" ;-)
Ciro Santilli 郝海东 han han 六四 法轮功 法轮功

9

आपको एक स्ट्रिंग स्थिरांक की सामग्री को बदलने की अनुमति नहीं है, जो कि पहले pबिंदु है। दूसरा pएक स्ट्रिंग है जिसे स्ट्रिंग स्ट्रिंग के साथ आरंभीकृत किया जाता है, और आप इसकी सामग्री को बदल सकते हैं।


6

इस तरह के मामलों के लिए, प्रभाव समान है: आप वर्णों के एक स्ट्रिंग में पहले चरित्र के पते को समाप्त करते हैं।

हालांकि घोषणाएं स्पष्ट रूप से समान नहीं हैं।

निम्नलिखित स्ट्रिंग के लिए मेमोरी को अलग करता है और एक कैरेक्टर पॉइंटर को भी सेट करता है, और उसके बाद स्ट्रिंग में पहले कैरेक्टर को इंगित करने के लिए पॉइंटर को इनिशियलाइज़ करता है।

char *p = "hello";

जबकि निम्नलिखित स्ट्रिंग के लिए मेमोरी को अलग करता है। तो यह वास्तव में कम मेमोरी का उपयोग कर सकता है।

char p[10] = "hello";

codeplusplus.blogspot.com/2007/09/… "" हालाँकि, वेरिएबल को इनिशियलाइज़ करने से एरीज़ के लिए बहुत बड़ा परफॉर्मेंस और स्पेस पेनल्टी
लगती है

@ एलिफ: मुझे लगता है कि यह निर्भर करता है कि चर कहां स्थित है। यदि यह स्थिर मेमोरी में है, तो मुझे लगता है कि यह EXE छवि में संग्रहीत किए जाने वाले सरणी और डेटा के लिए संभव है और किसी भी आरंभीकरण की आवश्यकता नहीं है। अन्यथा, हाँ, यह निश्चित रूप से धीमा हो सकता है यदि डेटा को आवंटित किया जाना है और फिर स्थैतिक डेटा को कॉपी करना है।
जोनाथन वुड

3

जहाँ तक मुझे याद है, एक सरणी वास्तव में संकेतकर्ताओं का एक समूह है। उदाहरण के लिए

p[1]== *(&p+1)

एक सच्चा कथन है


2
मैं एक सरणी का वर्णन मेमोरी के ब्लॉक के पते के लिए एक पॉइंटर होने के रूप में करूंगा। इसलिए *(arr + 1)आपको दूसरे सदस्य में क्यों लाता है arr। यदि *(arr)32-बिट मेमोरी पते के लिए अंक , उदाहरण के लिए bfbcdf5e, तो (दूसरा बाइट) *(arr + 1)को इंगित करता है bfbcdf60। इसलिए यदि OS segfault नहीं करता है, तो एक सरणी के दायरे से बाहर जाने पर अजीब परिणाम आएंगे। यदि int a = 24;पते पर है bfbcdf62, तो पहुंच arr[2]वापस हो सकती है 24, यह मानते हुए कि पहले सेगफॉल्ट नहीं होता है।
ब्रैडेन बेस्ट

3

से APUE , धारा 5.14:

char    good_template[] = "/tmp/dirXXXXXX"; /* right way */
char    *bad_template = "/tmp/dirXXXXXX";   /* wrong way*/

... पहले टेम्पलेट के लिए, स्टैक पर नाम आवंटित किया गया है, क्योंकि हम एक सरणी चर का उपयोग करते हैं। हालांकि, दूसरे नाम के लिए, हम एक पॉइंटर का उपयोग करते हैं। इस मामले में, केवल पॉइंटर के लिए मेमोरी स्टैक पर रहती है; संकलक स्ट्रिंग के लिए निष्पादन योग्य के केवल-पढ़ने वाले खंड में संग्रहीत करने की व्यवस्था करता है। जब mkstempफ़ंक्शन स्ट्रिंग को संशोधित करने का प्रयास करता है, तो एक विभाजन दोष होता है।

उद्धृत पाठ @Ciro Santilli की व्याख्या से मेल खाता है।


1

char p[3] = "hello"? char p[6] = "hello"याद रखना चाहिए कि सी में एक "स्ट्रिंग" के अंत में एक '\ 0' चार है।

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

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