2016-02-23 5 views
6

कुछ सी कोड लिखते समय, मुझे एक छोटी सी समस्या आई, जहां मुझे एक चरित्र को "स्ट्रिंग" में परिवर्तित करना पड़ा (कुछ मेमोरी रंक जिसकी शुरुआत char* पॉइंटर द्वारा दी गई है)।क्या यह (char *) और x कास्ट का व्यवहार अच्छी तरह परिभाषित है?

विचार यह है कि अगर कुछ sourcestr सूचक सेट किया गया है (नहीं NULL), तो मैं इसे अपने "अंतिम स्ट्रिंग" के रूप में, का उपयोग करना चाहिए अन्यथा मैं एक सरणी के प्रथम वर्ण में एक दिया charcode परिवर्तित करना चाहिए, और बजाय इसका इस्तेमाल है ।

इस प्रश्न के प्रयोजनों के लिए, हम मान लेंगे कि चर के प्रकार पहले से नहीं बदला जा सकता है। दूसरे शब्दों में, मैं सिर्फ charcode को के रूप में int के बजाय स्टोर नहीं कर सकता।

क्योंकि मैं आलसी हूं, मैंने खुद को सोचा: "अरे, क्या मैं सिर्फ चरित्र के पते का उपयोग नहीं कर सकता और उस सूचक को स्ट्रिंग के रूप में मान सकता हूं?"। यहाँ मैं क्या लिखा का एक छोटा टुकड़ा है (बस अभी तक दीवार के खिलाफ मेरे सिर तोड़ नहीं है!):

int charcode = FOO; /* Assume this is always valid ASCII. */ 

char* sourcestr = "BAR"; /* Case #1 */ 
char* sourcestr = NULL; /* Case #2 */ 

char* finalstr = sourcestr ? sourcestr : (char*)&charcode; 

अब निश्चित रूप से मैं इसे करने की कोशिश की है, और जैसा कि मैंने उम्मीद है, यह काम करता है। कुछ चेतावनी झंडे के साथ भी, कंपाइलर अभी भी खुश है। हालांकि, मेरे पास यह अजीब लग रहा है कि यह वास्तव में अपरिभाषित व्यवहार है, और मुझे बस यह नहीं करना चाहिए।

कारण मुझे ऐसा लगता है क्योंकि char* सरणी को तारों के रूप में ठीक से मुद्रित करने के लिए सरणी को समाप्त करने की आवश्यकता है (और मैं चाहता हूं कि मेरा हो!)। फिर भी, मुझे कोई निश्चितता नहीं है कि &charcode + 1 पर मान शून्य होगा, इसलिए मैं कुछ बफर ओवरफ़्लो पागलपन के साथ समाप्त हो सकता हूं।

क्या कोई वास्तविक कारण है कि यह ठीक से काम क्यों करता है, या मैंने कोशिश की है कि मैं सही जगहों पर शून्यों को पाने के लिए भाग्यशाली रहा हूं?

(ध्यान दें कि मैं अन्य तरीकों से रूपांतरण प्राप्त करने के लिए नहीं देख रहा हूँ। मैं बस एक char tmp[2] = {0} चर इस्तेमाल कर सकते हैं, और सूचकांक 0. पर मेरे चरित्र डाल मैं भी sprintf या snprintf, बशर्ते मैं की तरह कुछ इस्तेमाल कर सकते हैं 'बफर अतिप्रवाह के साथ पर्याप्त सावधान हूँ तरीके यह करने के लिए के असंख्य, मैं सिर्फ इस विशेष डाली आपरेशन के व्यवहार में दिलचस्पी रखता हूँ)

संपादित है:।। मैंने देखा है कुछ लोग इस फोन हैकरी, और चलो स्पष्ट हो: मैं पूरी तरह से आपसे सहमत हूं। मैं रिलीज कोड में वास्तव में ऐसा करने के लिए एक मासोचिस्ट के लिए पर्याप्त नहीं हूं। यह सिर्फ मुझे उत्सुक हो रहा है;)

+0

2 मामले में, यदि आप उस सूचक को स्ट्रिंग के रूप में प्रिंट कर रहे हैं तो नहीं, यह परिभाषित नहीं किया गया है। – 2501

+0

'char *' एक सूचक है। सी में एक स्ट्रिंग प्रकार नहीं है। अच्छे कारणों से कंपाइलर चेतावनी मौजूद है। उन्हें ध्यान देना। – Olaf

+0

अपने आप में और यूबी नहीं है, हालांकि जब आप इसे एक स्ट्रिंग की तरह मानते हैं (जैसे इसे printf या समान रूप से प्रिंट करना) तो यह यूबी – Magisch

उत्तर

0

ऐसा निम्न कारणों से पूरी तरह से अपरिभाषित व्यवहार है:

  1. कम संभावित है, लेकिन विचार करने के लिए जब सख्ती से मानकों के संदर्भित: आप मशीन/सिस्टम पर sizeof पूर्णांक कल्पना नहीं कर सकते जहां कोड हो जाएगा संकलित
  2. ऊपर जैसा कि आप कोडसेट नहीं मान सकते हैं। जैसे ईबीसीडीआईसी मशीन/सिस्टम पर क्या होता है?
  3. यह कहना आसान है कि आपकी मशीन में थोड़ा एंडियन प्रोसेसर है। बड़ी एंडियन मशीनों पर कोड बड़े एंडियन मेमोरी लेआउट के कारण विफल रहता है।
  4. क्योंकि कई प्रणालियों char पर, एक हस्ताक्षरित पूर्णांक है के रूप में, int है जब अपने चार एक नकारात्मक मूल्य (यानी 8bits char होने मशीनों पर char>127), यह विस्तार हस्ताक्षर करने के लिए यदि आप के रूप में मान असाइन कारण विफल हो सकता है

    char ch = FOO; 
    int charcode = ch; 
    

    पुनश्च: नीचे

कोड कोड बिंदु 3 के बारे में: आपकी स्ट्रिंग वास्तव में sizeof(int)>sizeof(char) और char वाले सकारात्मक एंड्रॉइड मशीन में होने वाली छोटी एंडियन मशीन में समाप्त हो जाएगी, क्योंकि इंटेल का एमएसबी 0 होगा और इस तरह के अंतहीनता के लिए मेमोरी लेआउट एलएसबी-एमएसबी (एलएसबी पहले)।

+0

यदि एन्कोडिंग ईबीसीडीआईसी थी तो इससे कोई फर्क क्यों पड़ता है? जब तक फू के मूल स्रोत एक अक्षर शाब्दिक थे या एक चरित्र fgetc के रूप में पढ़ा गया था, यह ठीक होना चाहिए। – rici

+0

@rici सिर्फ इसलिए कि ईसीआईडीआईसी में एसीआईआई में कुछ प्रतीक अपरिपक्व पूर्णांक नकारात्मक हो सकते हैं। शायद यह सावधानी से अधिक है :-) –

5

आपका कोड अच्छी तरह परिभाषित है क्योंकि आप हमेशा char* पर जा सकते हैं। लेकिन कुछ मुद्दों: "BAR" है

  1. ध्यान दें कि एक const char* शाब्दिक - तो सामग्री को संशोधित करने का प्रयास नहीं करते। अपरिभाषित होगा।

  2. सी मानक पुस्तकालय में किसी भी स्ट्रिंग फ़ंक्शंस के पैरामीटर के रूप में (char*)&charcode का उपयोग करने का प्रयास न करें। यह शून्य-समाप्त हो जाएगा। तो उस अर्थ में, आप इसे स्ट्रिंग के रूप में नहीं मान सकते हैं।(char*)&charcodeपर

  3. सूचक अंकगणित और पिछले अदिश charcode एक सहित अप करने के लिए मान्य होगा। लेकिन dereferencecharcode से परे कोई भी सूचक नहीं है। n की सीमा जिसके लिए अभिव्यक्ति (char*)&charcode + n मान्य है sizeof(int) पर निर्भर करता है।

3

कास्ट और असाइनमेंट, char* finalstr = (char*)&charcode; परिभाषित किया गया है।

प्रिंटिंग finalstr प्रिंटिंग के साथ स्ट्रिंग के रूप में %s, यदि यह charcode इंगित करता है तो अपरिभाषित व्यवहार है।

एक प्रकार int में हैकर और छिपाने वाली स्ट्रिंग का उपयोग करने के बजाय, एक चुने हुए रूपांतरण फ़ंक्शन का उपयोग करके पूर्णांक में संग्रहीत मानों को परिवर्तित करें। एक संभावित उदाहरण है:

char str[32] = { 0 }; 
snprintf(str , 32 , "%d" , charcode); 
char* finalstr = sourcestr ? sourcestr : str; 

या जो भी अन्य (परिभाषित!) रूपांतरण आपको पसंद है उसका उपयोग करें।

+0

कड़ाई से बोलने वाली प्रिंटिंग केवल यूबी है यदि मान में शून्य बाइट नहीं है। यदि इसमें शून्य बाइट्स हैं, तो यह कार्यान्वित किया गया है कि यह यूबी है या नहीं। उदाहरण के लिए 'charcode' value' 'ए' ASCII वर्णमाला के साथ छोटी-अंत प्रणाली पर ठीक होगा। – user694733

+0

@ user694733 यह नहीं होगा। विशिष्ट% s को एक स्ट्रिंग प्राप्त होनी चाहिए, यानी 'वर्ण प्रकार' की एक सरणी, जो 'int' टाइप नहीं है, अन्यथा व्यवहार अपरिभाषित है। 7.21.6.1 देखें। अनुच्छेद 8. और 9 (भले ही int बाइट्स के होते हैं, यह चरित्र प्रकार की सरणी नहीं है।) – 2501

+0

@ 2501: सी 11 मसौदा मानक '6.5 अभिव्यक्तियां, धारा 7 एक ऑब्जेक्ट का संग्रहित मूल्य केवल एक लवली अभिव्यक्ति द्वारा उपयोग किया जाएगा निम्न प्रकारों में से एक: [...] - एक चरित्र प्रकार .'। आप * सब कुछ * 'char' की सरणी के रूप में व्यवहार कर सकते हैं। बाइट-ऑर्डर कार्यान्वयन-निर्भर होगा, लेकिन यह * अपरिभाषित * नहीं है, जब तक शून्य-समाप्ति न हो। – EOF

2

अन्य लोगों की तरह यह भी काम करता है क्योंकि आपकी मशीन पर एक इंट का आंतरिक प्रतिनिधित्व थोड़ा अंत है और आपका char int से छोटा है। इसके अलावा आपके चरित्र का एसीआईआई मूल्य 128 से नीचे है या आपके पास हस्ताक्षर किए गए वर्ण हैं (अन्यथा साइन एक्सटेंशन होगा)। इसका मतलब है कि चरित्र का मूल्य int के प्रतिनिधित्व के निचले बाइट (ओं) में है और शेष int सभी शून्य (एक int का कोई सामान्य प्रतिनिधित्व मान लेना) होगा। आप "भाग्यशाली" नहीं हैं, आपके पास एक सुंदर सामान्य मशीन है।

यह स्ट्रिंग की अपेक्षा रखने वाले किसी भी फ़ंक्शन को उस चार सूचक को देने के लिए पूरी तरह से अपरिभाषित व्यवहार भी है।आप अब इसके साथ दूर हो सकते हैं लेकिन संकलक कुछ पूरी तरह से अलग करने के लिए अनुकूलित करने के लिए स्वतंत्र है।

उदाहरण के लिए यदि आप एक printf सिर्फ इतना है कि काम के बाद, संकलक मुक्त ग्रहण करने के लिए है कि आप हमेशा printf करने के लिए एक मान्य स्ट्रिंग पारित करेंगे जिसका मतलब है कि sourcestr शून्य होने के लिए जाँच अनावश्यक है क्योंकि अगर sourcestr शून्य था है printf कुछ ऐसा कहा जाएगा जो एक स्ट्रिंग नहीं है और संकलक यह मानने के लिए स्वतंत्र है कि अपरिभाषित व्यवहार कभी नहीं होता है। जिसका अर्थ यह है कि उस असाइनमेंट से पहले या उसके बाद sourcestr की कोई भी जांच अनावश्यक है क्योंकि संकलक पहले से ही जानता है कि यह पूर्ण नहीं है। इस धारणा को आपके कोड में हर जगह फैलाने की अनुमति है।

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

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

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