तार के रूप में चरित्र सरणियों का उपयोग कैसे किया जाना चाहिए?


10

मैं समझता हूं कि सी में तार सिर्फ कैरेक्टर एरे हैं। इसलिए मैंने निम्नलिखित कोड की कोशिश की, लेकिन यह अजीब परिणाम देता है, जैसे कचरा आउटपुट या प्रोग्राम क्रैश:

#include <stdio.h>

int main (void)
{
  char str [5] = "hello";
  puts(str);
}

यह काम क्यों नहीं करता है?

यह सफाई से संकलित करता है gcc -std=c17 -pedantic-errors -Wall -Wextra


नोट: यह पद एक स्ट्रिंग घोषित करते समय NUL टर्मिनेटर के लिए कमरा आवंटित करने में विफलता से उपजी समस्याओं के लिए एक कैनोनिकल एफएक्यू के रूप में उपयोग किया जाता है।

जवाबों:


12

एसी स्ट्रिंग एक वर्ण सरणी है जो एक शून्य टर्मिनेटर के साथ समाप्त होती है

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

इसलिए, हर बार जब आप एक स्ट्रिंग के लिए कमरा आवंटित करते हैं, तो आपको शून्य टर्मिनेटर वर्ण के लिए पर्याप्त स्थान शामिल करना चाहिए। आपका उदाहरण ऐसा नहीं करता है, यह केवल 5 वर्णों के लिए कमरा आवंटित करता है "hello"। सही कोड होना चाहिए:

char str[6] = "hello";

या समकक्ष, आप 5 वर्णों के लिए स्व-दस्तावेजीकरण कोड लिख सकते हैं और 1 शून्य टर्मिनेटर:

char str[5+1] = "hello";

रन-टाइम में गतिशील रूप से एक स्ट्रिंग के लिए मेमोरी आवंटित करते समय, आपको नल टर्मिनेटर के लिए कमरा भी आवंटित करना होगा:

char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);

यदि आप एक स्ट्रिंग के अंत में एक शून्य टर्मिनेटर संलग्न नहीं करते हैं, तो एक स्ट्रिंग की उम्मीद पुस्तकालय कार्य ठीक से काम नहीं करेगा और आपको कचरा उत्पादन या प्रोग्राम क्रैश जैसे "अपरिभाषित व्यवहार" कीड़े मिलेंगे।

सी में एक शून्य टर्मिनेटर चरित्र लिखने का सबसे आम तरीका एक तथाकथित "ऑक्टल एस्केप सीक्वेंस" का उपयोग करके है, इस तरह दिख रहा है '\0':। यह लिखने के लिए 100% के बराबर है 0, लेकिन \यह स्पष्ट करने के लिए स्व-दस्तावेजीकरण कोड के रूप में कार्य करता है कि शून्य स्पष्ट रूप से एक शून्य टर्मिनेटर है। कोड जैसे कि if(str[i] == '\0')जांच करेगा कि क्या विशिष्ट वर्ण शून्य टर्मिनेटर है।

कृपया ध्यान दें कि शून्य टर्म टर्मिनेटर का अशक्त संकेत या NULLमैक्रो से कोई लेना-देना नहीं है ! यह भ्रामक हो सकता है - बहुत समान नाम लेकिन बहुत भिन्न अर्थ। यही कारण है कि अशक्त टर्मिनेटर को कभी-कभी NULएक एल के रूप में संदर्भित किया जाता है , न कि नुकीले बिंदुओं के साथ भ्रमित होने के लिए NULL। अधिक विवरण के लिए इस SO प्रश्न के उत्तर देखें।

"hello"अपने कोड में कहा जाता है स्ट्रिंग शाब्दिक । इसे केवल-पढ़ने के लिए स्ट्रिंग माना जाता है। ""वाक्य रचना का मतलब है कि संकलक शाब्दिक स्वचालित रूप से स्ट्रिंग के अंत में एक अशक्त टर्मिनेटर संलग्न कर देगा। इसलिए यदि आप प्रिंट आउट sizeof("hello")लेते हैं तो आपको 6 मिलेगा, 5 नहीं, क्योंकि आपको एक शून्य टर्मिनेटर सहित सरणी का आकार मिलता है।


यह gcc के साथ सफाई से संकलित करता है

दरअसल, चेतावनी भी नहीं। यह सी भाषा में एक सूक्ष्म विस्तार / दोष के कारण है जो चरित्र सरणियों को एक स्ट्रिंग शाब्दिक के साथ आरंभ करने की अनुमति देता है जिसमें बिल्कुल वर्ण होते हैं जैसे कि सरणी में कमरा है और फिर चुपचाप अशक्त टर्मिनेटर को छोड़ दें (C17 6.7.9 / 15)। ऐतिहासिक कारणों से भाषा जानबूझकर इस तरह का व्यवहार कर रही है, विवरण के लिए स्ट्रिंग आरंभीकरण के लिए असंगत जीसी निदान देखें। यह भी ध्यान दें कि C ++ यहां अलग है और इस चाल / दोष का उपयोग करने की अनुमति नहीं देता है।


1
आपको char str[] = "hello";मामले का उल्लेख करना चाहिए ।
Jabberwocky

@ जैबरवॉकी यह एक सामुदायिक विकी है, संपादित करने और योगदान देने के लिए स्वतंत्र महसूस करें।
लुंडिन

1
... और शायद char *str = "hello";... str[0] = foo;समस्या भी ।
Jabberwocky

शायद sizeofफ़ंक्शन पैरामीटर पर इसके उपयोग का उपयोग करने के निहितार्थ का विस्तार करें , खासकर जब एक सरणी के रूप में परिभाषित किया गया हो।
वेन

@WeatherVane को यहां अन्य FAQ द्वारा कवर किया जाना चाहिए: stackoverflow.com/questions/492384/…
Lundin

4

C मानक से (7.1.1 शब्दों की परिभाषा)

1 एक स्ट्रिंग वर्णों का एक सन्निहित अनुक्रम है, जिसमें पहले अशक्त चरित्र को शामिल किया गया है। मल्टीबाइट स्ट्रिंग शब्द का उपयोग कभी-कभी स्ट्रिंग में निहित मल्टीबाइट पात्रों को दिए गए विशेष प्रसंस्करण पर जोर देने या विस्तृत स्ट्रिंग के साथ भ्रम से बचने के लिए किया जाता है। एक स्ट्रिंग के लिए एक सूचक अपने प्रारंभिक (सबसे कम संबोधित) चरित्र के लिए एक संकेतक है। एक स्ट्रिंग की लंबाई अशक्त चरित्र से पहले बाइट्स की संख्या है और एक स्ट्रिंग का मूल्य क्रम में निहित वर्णों के मूल्यों का अनुक्रम है।

इस घोषणा में

char str [5] = "hello";

स्ट्रिंग शाब्दिक "hello"की तरह आंतरिक प्रतिनिधित्व है

{ 'h', 'e', 'l', 'l', 'o', '\0' }

इसलिए इसमें 6 अक्षर हैं जिनमें शून्य को समाप्त करना शामिल है। इसके तत्वों का उपयोग वर्ण सरणी को शुरू करने के लिए किया जाता है strजो केवल 5 वर्णों के लिए स्थान आरक्षित करता है।

C मानक (C ++ मानक के विपरीत) एक चरित्र सरणी के ऐसे आरंभण की अनुमति देता है जब एक स्ट्रिंग शाब्दिक के समापन शून्य को एक प्रारंभिक के रूप में उपयोग नहीं किया जाता है।

हालाँकि परिणामस्वरूप वर्ण सरणी strमें स्ट्रिंग नहीं है।

यदि आप चाहते हैं कि सरणी में एक स्ट्रिंग होगी जिसमें आप लिख सकते हैं

char str [6] = "hello";

या केवल

char str [] = "hello";

अंतिम स्थिति में वर्ण सरणी का आकार स्ट्रिंग शाब्दिक के आरंभिकों की संख्या से निर्धारित होता है जो 6 के बराबर है।


0

क्या सभी तारों को वर्णों की एक सरणी माना जा सकता है ( हां ), क्या सभी वर्ण सरणियों को तार ( नहीं ) माना जा सकता है ।

क्यों नहीं? और क्यों इससे फर्क पड़ता है?

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

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

C के सभी कार्य जो एक स्ट्रिंग की अपेक्षा करते हैं एक तर्क के रूप में वर्णों के अनुक्रम को शून्य-समाप्त किए जाने की अपेक्षा करते हैं । क्यों?

यह सभी स्ट्रिंग फ़ंक्शंस के काम करने के तरीके के साथ करना है। चूंकि लंबाई किसी सरणी, स्ट्रिंग-फ़ंक्शंस के भाग के रूप में शामिल नहीं है, इसलिए सरणी में आगे को स्कैन करें जब तक कि nul-character (जैसे '\0'- दशमलव के बराबर 0) नहीं मिला। ASCII तालिका और विवरण देखें । भले ही आप उपयोग कर रहे strcpy, strchr, strcspn, आदि .. सभी स्ट्रिंग कार्यों पर भरोसा करते हैं नुल-समाप्त चरित्र मौजूद होने को परिभाषित करता है कि वह स्ट्रिंग के अंत है।

से दो समान कार्यों की तुलना नुल-टर्मिनेटिंग चरित्र string.hके महत्व पर जोर देगी । उदाहरण के लिए:

    char *strcpy(char *dest, const char *src);

strcpyसमारोह बस प्रतियां से बाइट्स srcके लिए destजब तक नुल-समाप्त चरित्र पाया जाता है कह strcpyजहां पात्रों को कॉपी को रोकने के लिए। अब इसी तरह के समारोह में ले memcpy:

    void *memcpy(void *dest, const void *src, size_t n);

फ़ंक्शन एक समान ऑपरेशन करता है, लेकिन srcस्ट्रिंग होने के लिए पैरामीटर पर विचार या आवश्यकता नहीं करता है । चूँकि बाइट्स को कॉपी करने के लिए एक न्यूल-टर्मिनेटिंग कैरेक्टर तक पहुंचने तक memcpyबस स्कैन नहीं किया जा सकता , इसलिए इसे तीसरे पैरामीटर के रूप में कॉपी करने के लिए बाइट्स की एक स्पष्ट संख्या की आवश्यकता होती है। यह तीसरा पैरामीटर एक ही आकार की जानकारी प्रदान करता है, जब तक कि एक शून्य-समाप्ति वर्ण नहीं पाया जाता है, तब तक इसे स्कैन करके आसानी से प्राप्त किया जा सकता है।srcdestmemcpystrcpy

(जो यह भी जोर देता है कि क्या गलत हो जाता है strcpy(या किसी फ़ंक्शन को स्ट्रिंग की अपेक्षा करते हुए) यदि आप फ़ंक्शन को एक शून्य -टर्म स्ट्रिंग प्रदान करने में विफल रहते हैं - इसका कोई पता नहीं है कि आपके मेमोरी सेगमेंट के बाकी हिस्सों में कहां रुकना और खुशी से दौड़ना होगा अपरिभाषित व्यवहार का आह्वान तब तक करना जब तक कि एक शून्य चरित्र स्मृति में कहीं न कहीं पाया जाता है - या एक खंडन दोष)

यही कारण है कि क्यों एक उम्मीद कार्यों नुल-समाप्त स्ट्रिंग एक पास किया जाना चाहिए नुल-समाप्त स्ट्रिंग और यह क्यों महत्वपूर्ण है


0

Intuitively ...

एक सरणी को एक चर के रूप में सोचो (चीजों को रखती है) और एक मान के रूप में एक स्ट्रिंग (एक चर में रखा जा सकता है)।

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

हालाँकि स्ट्रिंग में स्ट्रिंग को स्टोर करना संभव है जो स्ट्रिंग की तुलना में बहुत बड़ा है।

ध्यान दें कि सामान्य असाइनमेंट और तुलना ऑपरेटर ( = == <आदि) काम नहीं करते हैं जैसा कि आप उम्मीद कर सकते हैं। लेकिन strxyzफ़ंक्शंस का परिवार बहुत करीब आता है, एक बार जब आप जानते हैं कि आप क्या कर रहे हैं। तार और सरणियों पर C FAQ देखें ।

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