2012-01-22 19 views
10

मुझे समझ में क्यों निम्नलिखित स्निपेट एक विभाजन गलती दे रहा है कोशिश कर रहा हूँ:strtok विभाजन गलती

void tokenize(char* line) 
{ 
    char* cmd = strtok(line," "); 

    while (cmd != NULL) 
    { 
     printf ("%s\n",cmd); 
     cmd = strtok(NULL, " "); 
    } 
} 

int main(void) 
{ 
    tokenize("this is a test"); 
} 

मुझे लगता है कि strtok (पता) वास्तव में स्ट्रिंग शाब्दिक पर tokenize नहीं है, लेकिन इस मामले में, line स्ट्रिंग "this is a test" जो आंतरिक रूप से char की एक सरणी है के लिए सीधे इंगित करता है। क्या किसी भी सरणी में कॉपी किए बिना line टोकनिंग है?

+2

दोस्त - "यह एक परीक्षा है" एक STRING लिटरल है। मतलब यह एक * केवल पढ़ने के लिए * "चार की सरणी" पढ़ें। आप कुछ प्लेटफ़ॉर्म पर क्रैश किए बिना इसे संशोधित करने का प्रयास करने से भी दूर हो सकते हैं। लेकिन यह निश्चित रूप से * किसी भी * मंच पर नो-नो है :) – paulsm4

उत्तर

14

समस्या यह है कि आप स्ट्रिंग अक्षर को संशोधित करने का प्रयास कर रहे हैं। ऐसा करने से आपके प्रोग्राम के व्यवहार को अपरिभाषित किया जा सकता है।

यह कहकर कि आपको स्ट्रिंग अक्षर को संशोधित करने की अनुमति नहीं है, एक ओवरम्प्लिफिकेशन है। यह कहकर कि स्ट्रिंग अक्षर const गलत है; वे नहीं हैं।

चेतावनी: डिग्रेशन निम्नानुसार है।

स्ट्रिंग शाब्दिक "this is a test" टाइप char[15] (लंबाई के लिए 14, '\0' को समाप्त करने के लिए प्लस 1) की अभिव्यक्ति है।अधिकांश संदर्भों में, इस सहित, ऐसी अभिव्यक्ति को char* टाइप करने के लिए, सरणी के पहले तत्व में एक सूचक में निहित रूप से परिवर्तित किया गया है।

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

कुछ कंपाइलर आपको इससे दूर जाने की अनुमति दे सकते हैं। आपका कोड वास्तव में शाब्दिक से संबंधित स्थिर सरणी को संशोधित कर सकता है (जो बाद में बहुत भ्रम पैदा कर सकता है)।

अधिकांश आधुनिक कंपाइलर, केवल सरणी को केवल पढ़ने के लिए स्मृति में संग्रहीत करेंगे - भौतिक रोम नहीं, बल्कि स्मृति के क्षेत्र में जो वर्चुअल मेमोरी सिस्टम द्वारा संशोधन से संरक्षित है। ऐसी स्मृति को संशोधित करने का प्रयास आम तौर पर एक सेगमेंटेशन गलती और प्रोग्राम क्रैश होता है।

तो स्ट्रिंग अक्षर const क्यों नहीं हैं? चूंकि आपको वास्तव में उन्हें संशोधित करने की कोशिश नहीं करनी चाहिए, इसलिए यह निश्चित रूप से समझ में आ जाएगा - और सी ++ स्ट्रिंग अक्षर const बनाता है। कारण ऐतिहासिक है। const कीवर्ड 1 9 8 9 एएनएसआई सी मानक द्वारा पेश किए जाने से पहले मौजूद नहीं था (हालांकि इससे पहले कुछ कंपेलरों द्वारा इसे लागू किया गया था)। तो एक पूर्व एएनएसआई कार्यक्रम इस प्रकार दिखाई देंगे:

#include <stdio.h> 

print_string(s) 
char *s; 
{ 
    printf("%s\n", s); 
} 

main() 
{ 
    print_string("Hello, world"); 
} 

तथ्य यह है कि print_string संशोधित करने के लिए स्ट्रिंग s द्वारा की ओर इशारा अनुमति नहीं है लागू करने के लिए कोई तरीका नहीं था। एएनएसआई सी में स्ट्रिंग अक्षर const बनाना मौजूदा कोड तोड़ दिया होगा, जिसने एएनएसआई सी समिति ने ऐसा करने से बचने के लिए बहुत मेहनत की। तब से भाषा में ऐसा बदलाव करने के लिए एक अच्छा अवसर नहीं रहा है। (सी ++ के डिजाइनर, ज्यादातर बजेर्न स्ट्राउस्ट्रप, सी के साथ पिछड़े संगतता के बारे में चिंतित नहीं थे।

+0

महान स्पष्टीकरण !!! – ademar111190

+1

क्या डाउनवॉटर की व्याख्या करने की देखभाल होगी? –

2

जैसा कि आपने कहा, आप एक स्ट्रिंग शाब्दिक है, जो है क्या strtok करता संशोधित नहीं कर सकते। आप

char str[] = "this is a test"; 
tokenize(str); 

यह सरणी str बनाता है और this is a test\0 साथ यह initialises, और tokenize को यह करने के लिए एक सूचक गुजरता है क्या करना है।

0

मुझे यकीन है कि आप इस के बारे में पीटा मिल जाएगा ... लेकिन "strtok()" स्वाभाविक असुरक्षित और पहुँच उल्लंघन जैसी चीजों के होने का खतरा है।

यहाँ, इस सवाल का जवाब लगभग निश्चित रूप से एक स्ट्रिंग निरंतर उपयोग कर रहा है।

ऐसा करें:

void tokenize(char* line) 
{ 
    char* cmd = strtok(line," "); 

    while (cmd != NULL) 
    { 
     printf ("%s\n",cmd); 
     cmd = strtok(NULL, " "); 
    } 
} 

int main(void) 
{ 
    char buff[80]; 
    strcpy (buff, "this is a test"); 
    tokenize(buff); 
} 
+1

यदि आप स्ट्रोक की असुरक्षित प्रकृति को लाने जा रहे हैं, तो हम यह भी याद रख सकते हैं कि strncpy strcpy से कहीं अधिक सुरक्षित है। यद्यपि स्ट्रैपी एक संकलन-समय निरंतर स्ट्रिंग के लिए पूरी तरह से सुरक्षित है, फिर भी बाद में रिफैक्टरिंग स्ट्रैपी कॉल को बफर ओवरफ्लो भेद्यता में बदल सकती है। –

1

Strok ताकि इसे tokenize करने में अपना पहला तर्क संशोधित करता है। इसलिए आप इसे एक शाब्दिक स्ट्रिंग पारित नहीं हो सकता है, यह प्रकार const char * की अपरिभाषित व्यवहार के रूप में और बदला नहीं जा सकता है, इसलिए। आपको स्ट्रिंग अक्षर को एक चार सरणी में कॉपी करना होगा जिसे संशोधित किया जा सकता है।

2

वहाँ एक बहुत अच्छा कारण यह है कि एक संकलन समय निरंतर स्ट्रिंग एक विभाजन गलती का कारण होगा tokenize करने की कोशिश कर रहा है: निरंतर स्ट्रिंग केवल पढ़ने के लिए स्मृति में है।

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

1

आप अपने द्वारा क्या बिंदु बनाने की कोशिश कर रहे हैं "... आंतरिक रूप से char" टिप्पणी का एक सरणी है?

तथ्य यह है कि "this is a test" आंतरिक रूप से char की एक सरणी है जो कुछ भी नहीं बदलेगी। यह अभी भी एक स्ट्रिंग अक्षर है (सभी स्ट्रिंग अक्षर चार के गैर-संशोधित सरणी हैं)। आपका strtok अभी भी एक स्ट्रिंग अक्षर को टोकननाइज़ करने का प्रयास करता है। यही कारण है कि यह दुर्घटनाग्रस्त हो जाता है।

0

मैंने टोकन (cmd अपने मामले में) प्रिंट करने के लिए प्रिंटफ का उपयोग करने की कोशिश करने से सेगमेंटेशन फॉल्ट त्रुटि को दबाया पूर्ण हो गया

संबंधित मुद्दे