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




string constants (9)

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

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

तथा:

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

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

*(x + 1) = 'a';

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

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

char s[] = "hello";

या इस तरह:

char *s = "hello";

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


के मामले में:

char *x = "fred";

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

char x[] = "fred";

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


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

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

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

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

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

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


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

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

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


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

char *s = "Hello world";

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

करते हुए:

char s[] = "Hello world";

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

s[0] = 'J';

कानूनी।


यहां टिप्पणियों के प्रकाश में यह स्पष्ट होना चाहिए कि: 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


सी 99 एन 1256 ड्राफ्ट

सरणी अक्षर के दो पूरी तरह से अलग उपयोग हैं:

  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 भी संशोधित __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() {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

संकलन और decompile:

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

निष्कर्ष: जीसीसी स्टोर्स char* इसे .rodata अनुभाग में, .rodata में नहीं

अगर हम char[] लिए ऐसा करते हैं:

 char s[] = "abc";

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

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

इसलिए यह ढेर में संग्रहीत हो जाता है ( %rbp सापेक्ष)।

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

readelf -l a.out

जिसमें है:

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

char *str = "Hello";

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

char str[] = "Hello";

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

means str[0] = 'M';

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

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

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


char s[] = "hello";

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

char *s = "hello";

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





constants