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




string c-strings (3)

Intuitively ...

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

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

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

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

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

#include <stdio.h>

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

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

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

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


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

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

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

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

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

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

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

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

strcpy फंक्शन बस src से बाइट्स को कॉपी करने के लिए dest जब तक कि nul-terminating कैरेक्टर को strcpy जहां कॉपी करने वाले कैरेक्टर को रोकना है। अब इसी तरह के समारोह को memcpy :

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

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

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

यही कारण है कि एक शून्य-समाप्त स्ट्रिंग की अपेक्षा करने वाले कार्यों को एक शून्य-समाप्त स्ट्रिंग पारित किया जाना चाहिए और यह क्यों मायने रखता है


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

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

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

char str[6] = "hello";

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

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

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

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

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

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

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

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

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

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






nul