2009-01-17 9 views
35

मैं कुछ कोड के साथ काम कर रहा हूं जो एक स्थिर स्थानीय चर के लिए एक सूचक लौटने के मुहावरे का व्यापक रूप से उपयोग करता है। उदाहरण:एक स्थिर स्थानीय चर के लिए एक सूचक वापस लौट रहा है?

char* const GetString() 
{ 
    static char sTest[5]; 
    strcpy(sTest, "Test"); 
    return sTest; 
} 

क्या मैं यह सोचने में सही हूं कि यह सुरक्षित है?

पी एस, मुझे पता है कि यह एक ही बात करने का एक बेहतर तरीका होगा:

char* const GetString() 
{ 
    return "Test"; 
} 

संपादित करें: क्षमा याचना, समारोह हस्ताक्षर निश्चित रूप से होना चाहिए:

const char* GetString(); 

उत्तर

32

पहले उदाहरण:। कुछ हद तक सुरक्षित

char* const GetString() 
{ 
    static char sTest[5]; 
    strcpy(sTest, "Test"); 
    return sTest; 
} 

की सिफारिश की, यह हालांकि सुरक्षित है, फ़ंक्शन का दायरा समाप्त होने पर भी स्थिर चर का दायरा जीवित रहता है। यह फ़ंक्शन बिल्कुल थ्रेड-सुरक्षित नहीं है। GetString() फ़ंक्शन भरने के लिए एक बेहतर फ़ंक्शन आपको char* buffer और maxsize पास करने के लिए प्राप्त करेगा।

विशेष रूप से, इस समारोह एक रैत्रांत समारोह नहीं माना जाता है क्योंकि रैत्रांत कार्यों नहीं, अन्य बातों के साथ स्थिर (वैश्विक) गैर निरंतर डेटा को पता लौटना चाहिए। reentrant functions देखें।

दूसरा उदाहरण: पूरी तरह से असुरक्षित

char* const GetString() 
{ 
    return "Test"; 
} 

यह सुरक्षित हो अगर आप एक const char * किया जाएगा। जो आपने दिया वह सुरक्षित नहीं है। इसका कारण यह है कि स्ट्रिंग अक्षर को केवल पढ़ने के मेमोरी सेगमेंट में संग्रहीत किया जा सकता है और उन्हें संशोधित करने की इजाजत दी जाएगी, अपरिभाषित परिणाम होंगे।

char* const (कॉन्स्ट पॉइंटर) का अर्थ है कि आप उस पते को बदल नहीं सकते जो सूचक इंगित कर रहा है। const char * (पॉइंटर टू कॉन्स्ट) का अर्थ है कि आप उन तत्वों को नहीं बदल सकते हैं जो यह सूचक इंगित कर रहे हैं।

निष्कर्ष:

आप पर विचार करना चाहिए या तो:

1) आप कोड के लिए उपयोग किया है, तो GetString संशोधित एक char* buffer के पैरामीटर को भरने के लिए और एक maxsize उपयोग करने के लिए लेने के लिए।

2) यदि आपके पास कोड तक पहुंच नहीं है, लेकिन आपको इसे कॉल करना होगा, तो इस विधि को किसी अन्य फ़ंक्शन में लपेटें जो एक म्यूटेक्स द्वारा संरक्षित है। नई विधि जैसा वर्णन किया गया है 1.

6

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

+1

भी मजबूत हो सकता है "सामान्य तौर पर वे बचा जाना चाहिए", लेकिन यह निश्चित है कि आपको जोखिम और सीमाओं से अवगत होना चाहिए। _why_ पर स्पष्टता के लिए +1 ठीक है। – dmckee

+0

मैं डीएमकी के साथ सहमत हूं, पुन: प्रवेश समस्याएं फ़ंक्शन कॉल में जीवित रहने वाली स्थिरताओं के डिजाइन की वजह से हैं। यह बुरा व्यवहार नहीं है। लेकिन आपको वास्तव में जोखिम से अवगत होना चाहिए। –

0

हां, यह अक्सर कुछ लुकअप के टेक्स्ट हिस्से को वापस करने के लिए उपयोग किया जाता है, यानी कुछ त्रुटि संख्या को मानव अनुकूल स्ट्रिंग में अनुवाद करने के लिए उपयोग किया जाता है।

इसकी बुद्धिमान मामलों में यह करने के लिए आप जहां चाहते हैं:

fprintf(stderr, "Error was %s\n", my_string_to_error(error_code)); 

तो my_string_to_error() एक आवंटित स्ट्रिंग लौट आए, अपने कार्यक्रम से ऊपर (बहुत) इस तरह के एक समारोह के आम उपयोग को देखते हुए रिसाव होता है।

char const *foo_error(...) 
{ 
    return "Mary Poppins"; 
} 

... यह भी ठीक है, कुछ मस्तिष्क मृत कंपाइलर शायद आपको इसे डालना चाहते हैं।

बस, इस फैशन में तार घड़ी एक किताब :)

2

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

9

मूल रूप से, हां, यह इस अर्थ में सुरक्षित है कि मूल्य अनिश्चित काल तक चलेगा क्योंकि यह स्थैतिक है।

यह इस अर्थ में सुरक्षित नहीं है कि आपने निरंतर डेटा के लिए परिवर्तनीय सूचक के बजाय चर डेटा को निरंतर सूचकांक वापस कर दिया है। यदि बुला कार्यों डेटा को संशोधित करने की अनुमति नहीं है यह बेहतर है:

const char *GetString(void) 
{ 
    static char sTest[5]; 
    strncpy(sTest, "Test", sizeof(sTest)-1); 
    sTest[sizeof(sTest)-1] = '\0'; 
    return sTest; 
} 

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

const char *GetString(void) 
{ 
    static char sTest[5]; 
    strcpy_s(sTest, sizeof(sTest), "Test"); 
    return sTest; 
} 

इससे भी महत्वपूर्ण बात, दोनों वेरिएंट निरंतर डेटा के लिए एक (चर) सूचक लौटने के लिए, तो उपयोगकर्ता स्ट्रिंग को संशोधित नहीं जाना चाहिए और (शायद) के बाहर कुचल रही सरणी की सीमा। (जैसा कि @strager टिप्पणियों में इंगित करता है, const char * लौटने की गारंटी नहीं है कि उपयोगकर्ता लौटाए गए डेटा को संशोधित करने का प्रयास नहीं करेगा। हालांकि, उन्हें लौटा हुआ पॉइंटर डालना होगा, इसलिए यह गैर-कॉन्स है और फिर डेटा को संशोधित करें ; यह अपरिभाषित व्यवहार का आह्वान करता है और उस बिंदु पर कुछ भी संभव है।)

शाब्दिक वापसी का एक लाभ यह है कि नो-राइट वादा आमतौर पर संकलक और ऑपरेटिंग सिस्टम द्वारा लागू किया जा सकता है। स्ट्रिंग को प्रोग्राम के टेक्स्ट (कोड) सेगमेंट में रखा जाएगा, और ऑपरेटिंग सिस्टम एक गलती उत्पन्न करेगा (यूनिक्स पर सेगमेंटेशन उल्लंघन) यदि उपयोगकर्ता रिटर्न वैल्यू द्वारा इंगित डेटा को संशोधित करने का प्रयास करता है।

[कम से कम अन्य उत्तरों में से एक नोट करता है कि कोड पुन: प्रवेश नहीं किया गया है; वह सही है। शाब्दिक लौटने वाला संस्करण पुनः प्रवेशकर्ता है। तो फिर से entrancy महत्वपूर्ण है, इंटरफ़ेस ताकि फोन करने वाले जहां डाटा संग्रहित किया जाता है स्थान प्रदान करता है ठीक किया जाना आवश्यक]

+0

यह एक वादा नहीं है: यह एक सुझाव है। आप कॉन्स बंद कर सकते हैं। आप सही हैं कि यह शायद char * const के बजाय const char * होना चाहिए, लेकिन मुझे यकीन नहीं है कि फ़ंक्शन रिटर्न मानों के लिए अर्थ अलग है या नहीं। – strager

+0

@strager: हाँ, आप रिटर्न का दुरुपयोग करने में संकलक को मजबूर कर सकते हैं। 'Char * const' में, कॉन्स्ट का कोई लाभ नहीं होता है; एक बार मान को एक अलग (गैर-कॉन्स) पॉइंटर चर के लिए असाइन किया जाता है, तो उस सूचक को संशोधित किया जा सकता है; वापसी मूल्य कभी संशोधित नहीं किया जा सकता है। –

7

यह आपके द्वारा सुरक्षित रूप से क्या मतलब है इस पर निर्भर करता है। कुछ समस्याएं हैं जिन्हें मैं तुरंत देख सकता हूं:

  1. आपने char * const वापस कर दिया है, जो कॉलर्स को इस स्थान पर स्ट्रिंग को बदलने की अनुमति देगा। संभावित बफर ओवररन। या आपका मतलब const char * था?
  2. आपको पुनर्वित्त, या समवर्तीता के साथ कोई समस्या हो सकती है।

दूसरी व्याख्या करने के लिए, इस पर विचार करें:

const char * const format_error_message(int err) 
{ 
    static char error_message[MAXLEN_ERROR_MESSAGE]; 
    sprintf(error_message, "Error %#x occurred", err); 
    return error_message; 
} 

आप इसे इस प्रकार कॉल करते हैं:

int a = do_something(); 
int b = do_something_else(); 

if (a != 0 && b != 0) 
{ 
    fprintf(stderr, 
     "do_something failed (%s) AND do_something_else failed (%s)\n", 
     format_error_message(a), format_error_message(b)); 
} 

... क्या हो रहा है मुद्रित करने के लिए?

थ्रेडिंग के लिए ही।

+0

अच्छा उदाहरण - मैंने यह नहीं माना है! – strager

1

यह बहुत उपयोगी है, क्योंकि आप सीधे फ़ंक्शन का उपयोग printf पैरामीटर के रूप में कर सकते हैं। लेकिन, जैसा कि इसका उल्लेख किया गया था, एक कॉल के अंदर फ़ंक्शन में एकाधिक कॉल एक समस्या का कारण बनेंगे, क्योंकि फ़ंक्शन एक ही स्टोरेज का उपयोग करता है और इसे दो बार कॉल करने से लौटाई गई स्ट्रिंग को ओवरराइट कर दिया जाएगा। लेकिन मैंने कोड के इस टुकड़े का परीक्षण किया और ऐसा लगता है - आप सुरक्षा को एक फ़ंक्शन कॉल कर सकते हैं, जहां अधिकांश MAX_CALLS बार पर देनदारी का उपयोग किया जाता है और यह सही तरीके से व्यवहार करेगा।

#define MAX_CALLS 3 
#define MAX_LEN 30 

char *givemestring(int num) 
{ 
     static char buf[MAX_CALLS][MAX_LEN]; 
     static int rotate=0; 

     rotate++; 
     rotate%=sizeof(buf)/sizeof(buf[0]); 

     sprintf(buf[rotate],"%d",num); 
     return buf[rotate]; 

} 

केवल मुद्दा धागे की सुरक्षा है, लेकिन इस सूत्र स्थानीय चर (जीसीसी के __thread कीवर्ड) के साथ हल किया जा सकता

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