Char s[] और char*s के बीच क्या अंतर है?




string constants (11)

सी में, कोई इस तरह की घोषणा में एक स्ट्रिंग अक्षर का उपयोग कर सकता है:

char s[] = "hello";

या इस तरह:

char *s = "hello";

तो अंतर क्या है? मैं जानना चाहता हूं कि संकलन अवधि के दौरान वास्तव में क्या होता है, दोनों संकलन और रन टाइम पर।


यहां अंतर यह है कि

char *s = "Hello world";

मेमोरी के केवल पढ़ने वाले हिस्सों में "Hello world" रखेगा, और इसके लिए एक पॉइंटर बनाना इस स्मृति पर किसी भी लेखन ऑपरेशन को अवैध बनाता है।

करते हुए:

char s[] = "Hello world";

शाब्दिक स्ट्रिंग को केवल-पढ़ने वाली मेमोरी में रखता है और स्ट्रिंग पर नई आवंटित मेमोरी को स्ट्रिंग की प्रतिलिपि बनाता है। इस प्रकार बनाना

s[0] = 'J';

कानूनी।


char s[] = "Hello world";

यहां, वर्णों की एक सरणी है, जिसे हम चाहें तो अधिलेखित किया जा सकता है।

char *s = "hello";

एक स्ट्रिंग अक्षर का उपयोग स्मृति में कहीं भी इन वर्ण ब्लॉक बनाने के लिए किया जाता है, जो इस सूचक का संकेत दे रहा है। हम यहां उस ऑब्जेक्ट को पुन: असाइन कर सकते हैं जो इसे बदलकर इंगित कर रहा है, लेकिन जब तक यह एक स्ट्रिंग अक्षर को इंगित करता है तब तक वर्णों के ब्लॉक को बदल दिया जा सकता है।


char *str = "Hello";

उपर्युक्त सेट "हैलो" को इंगित करने के लिए स्ट्रेट को इंगित करता है जो प्रोग्राम की बाइनरी छवि में हार्ड-कोड किया गया है, जिसे केवल स्मृति में पढ़ने के रूप में चिह्नित किया गया है, इसका मतलब है कि इस स्ट्रिंग अक्षर में कोई भी बदलाव अवैध है और इससे विभाजन त्रुटियां फेंक जाएंगी।

char str[] = "Hello";

स्टैक पर नई आवंटित स्मृति को स्ट्रिंग की प्रतिलिपि बनाता है। इस प्रकार इसमें कोई बदलाव करने की अनुमति है और कानूनी है।

means str[0] = 'M';

स्ट्रिंग को "मेलो" में बदल देगा।

अधिक जानकारी के लिए, कृपया इसी तरह के प्रश्न से गुज़रें:

"Char * s" के साथ शुरू की गई स्ट्रिंग पर लिखते समय मुझे विभाजन खंड क्यों मिलता है लेकिन "char s []" नहीं?


बस जोड़ने के लिए: आपको उनके आकार के लिए अलग-अलग मान भी मिलते हैं।

printf("sizeof s[] = %zu\n", sizeof(s));  //6
printf("sizeof *s  = %zu\n", sizeof(s));  //4 or 8

जैसा ऊपर बताया गया है, एक सरणी '\0' लिए अंतिम तत्व के रूप में आवंटित किया जाएगा।


चार एस [] = "हैलो";

इस "एस" में स्मृति में स्मृति शामिल है और उपयोगकर्ता किसी भी समय संशोधित कर सकता है। इसका कानूनी

एस [0] = 'आर'; // r "हेलो" में 'h' को प्रतिस्थापित करता है

एस [1] = 'क्यू'; // q हैलो में 'e' को प्रतिस्थापित करता है

चार * एस = "हैलो";

कोड या टेक्स्ट सेगमेंट में संग्रहीत "हैलो" और कोड या टेक्स्ट सेगमेंट का पता 's' पर वापस आ गया है। यहां हम कोड या टेक्स्ट डेटा को बदलने में सक्षम नहीं हैं।


यहां टिप्पणियों के प्रकाश में यह स्पष्ट होना चाहिए कि: char * s = "हैलो"; एक बुरा विचार है, और बहुत संकीर्ण क्षेत्र में इस्तेमाल किया जाना चाहिए।

यह इंगित करने का एक अच्छा अवसर हो सकता है कि "दृढ़ता" एक "अच्छी बात" है। जब भी और जहां भी आप कर सकते हैं, "कोड" कीवर्ड का उपयोग अपने कोड को "आराम से" कॉलर्स या प्रोग्रामर से बचाने के लिए करें, जो आमतौर पर पॉइंटर्स खेलने में आने पर सबसे अधिक "आराम" होते हैं।

पर्याप्त मेलोड्रामा, यहां "कॉन्स" के साथ एडॉर्निंग पॉइंटर्स के दौरान कोई क्या हासिल कर सकता है। (नोट: पॉइंटर्स घोषणाओं को दाएं से बाएं पढ़ने के लिए है।) पॉइंटर्स के साथ खेलते समय अपने आप को बचाने के लिए 3 अलग-अलग तरीके हैं:

const DBJ* p means "p points to a DBJ that is const" 

- यानी, डीबीजे ऑब्जेक्ट को पी के माध्यम से बदला नहीं जा सकता है।

DBJ* const p means "p is a const pointer to a DBJ" 

- यानी, आप डीबीजे ऑब्जेक्ट को पी के माध्यम से बदल सकते हैं, लेकिन आप पॉइंटर पी को स्वयं नहीं बदल सकते हैं।

const DBJ* const p means "p is a const pointer to a const DBJ" 

- यानी, आप पॉइंटर पी को स्वयं नहीं बदल सकते हैं, न ही आप डीबीजे ऑब्जेक्ट को पी के माध्यम से बदल सकते हैं।

प्रयास किए गए कॉन्स-चींटी उत्परिवर्तन से संबंधित त्रुटियां संकलित समय पर पकड़ी जाती हैं। कॉन्स के लिए कोई रनटाइम स्पेस या स्पीड पेनल्टी नहीं है।

(मान लीजिए कि आप निश्चित रूप से सी ++ कंपाइलर का उपयोग कर रहे हैं?)

--DBJ


एक अतिरिक्त के रूप में, इस बात पर विचार करें कि, केवल पढ़ने के उद्देश्यों के लिए दोनों का उपयोग समान है, आप [] या *(<var> + <index>) प्रारूप के साथ अनुक्रमणित करके एक char तक पहुंच सकते हैं:

printf("%c", x[1]);     //Prints r

तथा:

printf("%c", *(x + 1)); //Prints r

जाहिर है, अगर आप करने का प्रयास करते हैं

*(x + 1) = 'a';

आपको शायद सेगमेंटेशन फॉल्ट मिलेगा, क्योंकि आप केवल-पढ़ने वाली मेमोरी तक पहुंचने का प्रयास कर रहे हैं।


सबसे पहले, कार्य तर्कों में, वे बिल्कुल बराबर हैं:

void foo(char *x);
void foo(char x[]); // exactly the same in all respects

अन्य संदर्भों में, char * एक पॉइंटर आवंटित करता है, जबकि char [] एक सरणी आवंटित करता है। स्ट्रिंग पहले मामले में कहां जाती है, आप पूछते हैं? संकलक सचमुच स्ट्रिंग अक्षर को पकड़ने के लिए एक स्थिर अज्ञात सरणी आवंटित करता है। इसलिए:

char *x = "Foo";
// is approximately equivalent to:
static const char __secret_anonymous_array[] = "Foo";
char *x = (char *) __secret_anonymous_array;

ध्यान दें कि आपको कभी भी इस अज्ञात सरणी की सामग्री को इस सूचक के माध्यम से संशोधित करने का प्रयास नहीं करना चाहिए; प्रभाव अपरिभाषित हैं (अक्सर एक दुर्घटना का अर्थ है):

x[1] = 'O'; // BAD. DON'T DO THIS.

सरणी सिंटैक्स का उपयोग सीधे इसे नई मेमोरी में आवंटित करता है। इस प्रकार संशोधन सुरक्षित है:

char x[] = "Foo";
x[1] = 'O'; // No problem.

हालांकि सरणी केवल तब तक जीवित रहती है जब तक कि आप इसे किसी फ़ंक्शन में नहीं करते हैं, इसलिए इस सरणी में एक पॉइंटर वापस न करें या रिसाव न करें - इसके बजाय strdup() या इसी तरह की प्रतिलिपि बनाएं। यदि सरणी वैश्विक दायरे में आवंटित की जाती है, बेशक, कोई समस्या नहीं है।


घोषणाओं को देखते हुए

char *s0 = "hello world";
char s1[] = "hello world";

निम्नलिखित काल्पनिक स्मृति मानचित्र मानें:

                    0x01  0x02  0x03  0x04
        0x00008000: 'h'   'e'   'l'   'l'
        0x00008004: 'o'   ' '   'w'   'o'
        0x00008008: 'r'   'l'   'd'   0x00
        ...
s0:     0x00010000: 0x00  0x00  0x80  0x00
s1:     0x00010004: 'h'   'e'   'l'   'l'
        0x00010008: 'o'   ' '   'w'   'o'
        0x0001000C: 'r'   'l'   'd'   0x00

स्ट्रिंग शाब्दिक "hello world" स्थैतिक भंडारण अवधि के साथ char (सी ++ में const char ) का एक 12-तत्व सरणी है, जिसका अर्थ यह है कि कार्यक्रम शुरू होने पर आवंटित किया जाता है और प्रोग्राम समाप्त होने तक आवंटित रहता है। एक स्ट्रिंग शाब्दिक सामग्री की सामग्री को संशोधित करने का प्रयास अपरिभाषित व्यवहार का आह्वान करता है।

रेखा

char *s0 = "hello world";

स्वत: भंडारण अवधि के साथ char को पॉइंटर के रूप में परिभाषित करता है (जिसका अर्थ है कि वेरिएबल एस 0 केवल उस दायरे के लिए मौजूद है जिसमें इसे घोषित किया गया है) और स्ट्रिंग 0x00008000 (इस उदाहरण में 0x00008000 ) के पते की प्रतिलिपि बनाता है। ध्यान दें कि चूंकि एक स्ट्रिंग s0 लिए s0 अंक हैं, इसलिए इसे किसी भी फ़ंक्शन के लिए तर्क के रूप में उपयोग नहीं किया जाना चाहिए जो इसे संशोधित करने का प्रयास करेगा (उदाहरण के लिए, strtok() , strcat() , strcpy() , आदि)।

रेखा

char s1[] = "hello world";

स्वत: भंडारण अवधि के साथ s1 को 12-तत्व सरणी (लम्बाई स्ट्रिंग अक्षर से लिया जाता है) के रूप में परिभाषित करता है और शाब्दिक की सामग्री को सरणी में प्रतिलिपि बनाता है। जैसा कि आप मेमोरी मैप से देख सकते हैं, हमारे पास "hello world" स्ट्रिंग की दो प्रतियां हैं; अंतर यह है कि आप s1 में निहित स्ट्रिंग को संशोधित कर सकते हैं।

s0 और s1 अधिकांश संदर्भों में अंतर-परिवर्तनीय हैं; यहां अपवाद हैं:

sizeof s0 == sizeof (char*)
sizeof s1 == 12

type of &s0 == char **
type of &s1 == char (*)[12] // pointer to a 12-element array of char

आप परिवर्तनीय एस 0 को एक अलग स्ट्रिंग अक्षर या किसी अन्य चर पर इंगित करने के लिए पुन: असाइन कर सकते हैं। आप एक अलग सरणी को इंगित करने के लिए चर s1 को पुन: असाइन नहीं कर सकते हैं।


char s[] = "hello";

घोड़ों की एक सरणी घोषित करता है जो प्रारंभकर्ता (5 + 1 char एस) को पकड़ने के लिए काफी लंबा है और सरणी में दिए गए स्ट्रिंग के सदस्यों को प्रतिलिपि बनाकर सरणी को प्रारंभ करता है।

char *s = "hello";

एक या अधिक (इस मामले में अधिक) के लिए एक सूचक होने की घोषणा करता है और इसे सीधे एक निश्चित (केवल पढ़ने के लिए) स्थान पर इंगित करता है जिसमें शाब्दिक "hello"


के मामले में:

char *x = "fred";

एक्स एक lvalue - इसे असाइन किया जा सकता है। लेकिन इस मामले में:

char x[] = "fred";

एक्स एक लालसा नहीं है, यह एक रैल्यू है - आप इसे असाइन नहीं कर सकते हैं।





constants