2009-09-23 20 views
6

निम्नलिखित कोड में, मैं एक स्ट्रिंग को एक char * str में कॉपी करता हूं, जो strncpy() का उपयोग करके 10 वर्ण लंबा है।स्ट्रिंग न्यूल पर समाप्त नहीं होती है लेकिन फिर भी सामान्य रूप से व्यवहार करती है, क्यों?

अब strncpy() मैनुअल के अनुसार, "चेतावनी: अगर स्रोत के पहले एन बाइट्स में कोई शून्य बाइट नहीं है, तो नियत में रखी गई स्ट्रिंग को निरस्त नहीं किया जाएगा।" जो वास्तव में यहां होता है।

स्रोत स्ट्रिंग 26 वर्णक लंबी है और मैंने 10 वर्णों की प्रतिलिपि बनाई है, इसलिए स्ट्र के अंत में कोई शून्य चरित्र नहीं रखा गया है।

लेकिन जब मैं स्ट्र की सामग्री मुद्रित करता हूं, तब तक 0 से शुरू होने तक मुझे '\ 0' मिलता है, यह सामान्य रूप से व्यवहार करता है।

क्यों? जब अंत में कोई '\ 0' नहीं रखा जाता है तो लूप सही जगह पर क्यों रुकता है?

मुझे क्या समझता है कि इसे "सेगमेंटेशन गलती" देना चाहिए या कम से कम इसे वहां नहीं रोकना चाहिए और कुछ कचरे के मूल्यों को प्रिंट करना चाहिए।

 
str[ 0 ] has got : a 
str[ 1 ] has got : b 
str[ 2 ] has got : c 
str[ 3 ] has got : d 
str[ 4 ] has got : e 
str[ 5 ] has got : f 
str[ 6 ] has got : g 
str[ 7 ] has got : h 
str[ 8 ] has got : i 
str[ 9 ] has got : j 

किसी भी मदद की सराहना की जाएगी:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define SIZE 10 

int main() 
{ 
    char *str ; 
    str = malloc(sizeof(char) * SIZE); 
    if(str == NULL) 
     exit(1); 
    memset(str, 0, sizeof(char) * SIZE); 

    strncpy(str, "abcdefghijklmnopqrstuvwxyz", sizeof(char) * SIZE); 

    unsigned int index; 
    for(index = 0; str[ index ] != '\0' ; index++) { 
     printf("str[ %u ] has got : %c \n ", index, str[ index ]); 
    } 

    return 0; 
} 

यहाँ उत्पादन होता है।

संपादित

वहाँ जाँच करने के लिए एक उचित तरीका है एक स्ट्रिंग '\ 0' या नहीं पर समाप्त होता है या नहीं? मैंने हमेशा उपरोक्त लूप को अंतिम परीक्षण माना था, लेकिन अब ऐसा लगता है कि यह नहीं है।

आइए कहें कि हमें अन्य प्रोग्रामर द्वारा विकसित कुछ फ़ंक्शन से स्ट्रिंग मिलती है। अब हम कैसे जानेंगे कि यह सही जगह पर '\ 0' के साथ समाप्त होता है। ऐसा नहीं हो सकता है, तो यह वास्तविक आकार से आगे जाएगा जब तक कि हम कुछ '\ 0' प्राप्त न करें। हम स्ट्रिंग के वास्तविक आकार को कभी नहीं जानते।

तो हम ऐसी स्थिति से कैसे निपट सकते हैं?

कोई सुझाव?

+2

आकार (char) * हमेशा * 1 है !! – paxdiablo

+1

@ पैक्स: सच है, लेकिन यहां इसकी चर्चा है: http://stackoverflow.com/questions/1011806/is-it-necessary-to-multiply-by-sizeof-char-when-manipulating-memory – sharptooth

+0

strncmp लेता है पात्रों की संख्या, हालांकि स्ट्रिंग का आकार नहीं है, इसलिए इसका उपयोग करना गलत है। –

उत्तर

6

के रूप में आपके संपादन के लिए, मैं पंडिताऊ जा रहा है मदद मिलेगी कुछ मुद्दों पर स्पष्ट लगता है।

सी में स्ट्रिंग जैसी कोई चीज़ नहीं है। एक "सी स्ट्रिंग" की एक अवधारणा है जो सी मानक लाइब्रेरी के साथ काम करती है जिसे अक्षरों के एनयूएल समाप्त अनुक्रम से अधिक कुछ भी नहीं कहा जाता है, इसलिए वास्तव में ऐसी कोई चीज़ नहीं है जो "गैर-निरस्त समाप्त स्ट्रिंग" "सी में। इसलिए आपका प्रश्न बेहतर वाक्यांश है" मैं कैसे निर्धारित कर सकता हूं कि मनमाना वर्ण बफर वैध सी स्ट्रिंग है या नहीं? " या "मैं यह निर्धारित कैसे कर सकता हूं कि मुझे मिली स्ट्रिंग इच्छित स्ट्रिंग है"

दुर्भाग्यवश, पहले सवाल का जवाब केवल बफर को स्कैन करना है जब तक आप एक एनयूएल बाइट का सामना नहीं कर लेते। यह आपको सी स्ट्रिंग की लंबाई देगा।

दूसरे प्रश्न का कोई आसान जवाब नहीं है।इस तथ्य के कारण कि सी में लंबाई मेटाडेटा (या फंक्शन कॉल में सरणी के आकार के चारों ओर ले जाने की क्षमता) के साथ वास्तविक स्ट्रिंग प्रकार नहीं है, यह निर्धारित करने का कोई वास्तविक तरीका नहीं है कि ऊपर निर्धारित स्ट्रिंग लंबाई की लंबाई क्या है इच्छित स्ट्रिंग। यह स्पष्ट हो सकता है कि अगर हम प्रोग्राम में सेगफाल्ट या आउटपुट में "कचरा" देखना शुरू करते हैं, लेकिन आम तौर पर हम पहले एनयूएल बाइट तक स्कैनिंग द्वारा स्ट्रिंग ऑपरेशन कर रहे हैं (आमतौर पर स्ट्रिंग लम्बाई पर ऊपरी किनारे के साथ ताकि गन्दा से बचें बफर ओवररन त्रुटियां)

15

ऐसा होता है कि आवंटित ब्लॉक के अंत से बाहर एक शून्य बाइट है।

सबसे अधिक संभावना malloc() अधिक स्मृति आवंटित करता है और तथाकथित गार्ड मूल्यों है कि रिक्त बाइट्स को रोकने के लिए हो डालता है या यह कुछ मेटाडेटा बाद में free() द्वारा प्रयोग की जाने डालता है और इस मेटाडाटा कि स्थिति में एक अशक्त बाइट सही को रोकने के लिए होता है।

वैसे भी आपको इस व्यवहार पर भरोसा नहीं करना चाहिए। आपको शून्य चरित्र के लिए एक और बाइट अनुरोध करना है (malloc()) ताकि शून्य वर्ण स्थान भी आपको कानूनी रूप से आवंटित किया जा सके।

यदि कोई स्ट्रिंग ठीक से समाप्त हो जाती है तो परीक्षण करने के लिए कोई पोर्टेबल तरीका नहीं है। ऐसा हो सकता है कि आवंटित ब्लॉक के अंत में एक बार हो जाने पर आपका प्रोग्राम बस दुर्घटनाग्रस्त हो जाएगा। या ऐसा हो सकता है कि ब्लॉक के अंत से कहीं भी एक नल चरित्र है और आप गलत व्याख्या किए गए स्ट्रिंग में हेरफेर करते समय बाद में ब्लॉक के अंत से परे स्मृति को ओवरराइट करते हैं।

आदर्श रूप से आपको कुछ फ़ंक्शन की आवश्यकता है जो जांचता है कि आपको दिया गया पता और एक ही आवंटन से संबंधित है (शायद ब्लॉक का प्रारंभ)। यह धीमा होगा और इसके लायक नहीं होगा और ऐसा करने के लिए कोई मानक तरीका नहीं है।

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

+0

नहीं, ऐसा नहीं है। –

+0

हां स्ट्रिंग के अंत में यह एक शून्य बाइट होता है। यदि आप विभिन्न आकारों को आजमाते हैं तो आपको * खराब * आउटपुट मिलेगा। –

+0

तो यह जांचने का कोई मानक तरीका नहीं है कि स्ट्रिंग को समाप्त कर दिया गया है या नहीं। यह एक बुरी खबर है। मुझे लगता है कि आवेदन पर काम कर रहे सभी प्रोग्रामर कुछ मानक पर सहमत होना चाहिए। एक सूचक के पहले तीन चरित्र की तरह इसकी आकार बताएगी और चौथे से वास्तविक स्ट्रिंग शुरू हो जाएगी। –

4

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

आपके द्वारा आवंटित स्मृति '\0' बाइट सही जगह पर होती है। (उदाहरण के लिए, आप डीबग मोड में विजुअल C++ उपयोग कर रहे हैं, ढेर प्रबंधक शून्य आबंटित स्मृति से पहले ही यह अपने कार्यक्रम के लिए बाहर हाथ। लेकिन यह बस के रूप में अच्छी तरह से शुद्ध भाग्य हो सकता है।)

वहाँ एक है यह जांचने का उचित तरीका है कि कोई स्ट्रिंग '\0' पर समाप्त होती है या नहीं?

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

अब हम कैसे जानेंगे कि कुछ अन्य प्रोग्रामर द्वारा विकसित कुछ फ़ंक्शन से कुछ स्ट्रिंग '\0' के साथ सही जगह पर समाप्त होती है। ऐसा नहीं हो सकता है, तो यह वास्तविक आकार से आगे जाएगा जब तक हमें कुछ '\0' प्राप्त नहीं होता है। हम स्ट्रिंग के वास्तविक आकार को कभी नहीं जानते।

तो हम ऐसी स्थिति से कैसे निपट सकते हैं?

आप नहीं कर सकते। यदि दूसरा फ़ंक्शन इसे खराब करता है, तो आप उस खराब को खराब कर देते हैं।

+0

ढेर मैनेजर शून्य के बारे में स्मृति: माइक्रोसॉफ्ट कंपाइलर शून्य स्मृति नहीं है (एन डीबग या रिलीज बिल्ड)। डीबग ढेर का उपयोग करते समय एमएसवीसी रनटाइम आवंटित स्मृति को 0xCD बाइट्स से भर देगा, शून्य नहीं। स्मृति को साफ़ करने के बजाय 'कचरा' भरना आमतौर पर समस्याओं को खोजने में अधिक प्रभावी होता है। इसके अलावा, आवंटन से पहले और बाद में स्मृति का कुछ हिस्सा 0xFD मानों से भरा जाएगा। Http://stackoverflow.com/questions/370195/when-and-why-will-an-os-initialise-memory-to-0xcd-0xdd-etc-on-malloc-free-new/370362#370362 –

+0

@ देखें माइकल: सभी के लिए मुझे पता है कि आप सही हो सकते हैं। लेकिन फिर भी, ISTR ने बार-बार पढ़ा है कि शून्य को शून्य नहीं किया जा रहा है, रिलीज संस्करणों को क्रैश होने का एक आम कारण है जबकि डीबग संस्करण वीसी में काम करते हैं। '<स्क्रैच हेड>' – sbi

0

शार्पतोथ ने व्यवहार के संभावित कारणों को समझाया है, इसलिए मैं इसे दोहराने वाला नहीं हूं।

जब बफ़र्स आवंटन, मैं हमेशा, एक बाइट द्वारा बिना आवंटित इस तरह:

#define SIZE 10 
char* buf = malloc(sizeof(char)*(SIZE+1)); 
/* error-check the malloc call here */ 
buf[SIZE] = '\0'; 
+0

एह, "आकार (चार) - (SIZE + 1)"? माइनस? –

+0

हम यह मेमेट (dest, 0, SIZE) भी कर सकते हैं; strncpy (dest, स्रोत, SIZE -1); इस तरह अंतिम बाइट शून्य होगा। –

+0

यह * - बार होना चाहिए। नया कीबोर्ड :) – gnud

0

आप भाग्यशाली हैं कि अंतरिक्ष के आवंटित क्षेत्र से शून्य शून्य हो।

इस कोड को अन्य सभी प्लेटफ़ॉर्म पर आज़माएं और आप देखेंगे कि यह वही व्यवहार नहीं कर सकता है।

0

मुझे लगता है कि sharptooth का जवाब सही है। आवंटित अधिक जगह हैं। मैं इस प्रकार कार्यक्रम को संशोधित:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define SIZE 10 

int main() 
{ 
    char *str ; 
    int *p; 
    int actual_length; 
    str = malloc(sizeof(char) * SIZE); 
    if(str == NULL) 
     exit(1); 

    actual_length = (int)*(str - 4) - 1 - 4; 
    printf("actual length of str is %d\n", actual_length); 
    p = (int*) malloc(sizeof(int)); 
    if (p == NULL) exit(1); 
    *p = -1; 
    char* pc = (char*)(p - 1); 
    pc [0] = 'z'; 
    pc [1] = 'z'; 
    pc [2] = 'z'; 
    pc [3] = 'z'; 

    memset(str, 0, sizeof(char) * SIZE); 

    memcpy(str, "abcdefghijklmnopqrstuvwxyz", sizeof(char) * SIZE); 

    int i; 
    for (i = SIZE; i < actual_length; i++) 
    str[i] = 'y'; 

    unsigned int index; 
    for(index = 0; str[ index ] != '\0' ; index++) { 
     printf("str[ %u ] has got : %c \n ", index, str[ index ]); 
    } 

    return 0; 
} 

उत्पादन

actual length of str is 12 
str[ 0 ] has got : a 
str[ 1 ] has got : b 
str[ 2 ] has got : c 
str[ 3 ] has got : d 
str[ 4 ] has got : e 
str[ 5 ] has got : f 
str[ 6 ] has got : g 
str[ 7 ] has got : h 
str[ 8 ] has got : i 
str[ 9 ] has got : j 
str[ 10 ] has got : y 
str[ 11 ] has got : y 
str[ 12 ] has got : z 
str[ 13 ] has got : z 
str[ 14 ] has got : z 
str[ 15 ] has got : z 
str[ 16 ] has got : \377 
str[ 17 ] has got : \377 
str[ 18 ] has got : \377 
str[ 19 ] has got : \377 

मेरे ओएस डेबियन निचोड़/sid है।

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