हम std :: string के c_str () से क्या प्रदर्शन की उम्मीद कर सकते हैं? हमेशा स्थिर समय?


13

मैं हाल ही में कुछ आवश्यक अनुकूलन कर रहा हूँ। एक चीज जो मैं कर रहा हूं, वह कुछ ओस्ट्रिंगस्ट्रीम -> स्प्रिंटफ्स को बदल रही है। मैं स्प्रिंटफिंग कर रहा हूं: एस डी का एक गुच्छा :: स्ट्रिंग से एसी शैली की सरणी, अला

char foo[500];
sprintf(foo, "%s+%s", str1.c_str(), str2.c_str());

यह पता चला है कि Microsoft का std :: string :: c_str () कार्यान्वयन निरंतर समय में चलता है (यह सिर्फ एक आंतरिक पॉइंटर लौटाता है)। ऐसा प्रतीत होता है कि libstdc ++ ऐसा ही करता है । मुझे पता है कि std c_str के लिए कोई गारंटी नहीं देता है, लेकिन ऐसा करने के दूसरे तरीके की कल्पना करना कठिन है। यदि, उदाहरण के लिए, उन्होंने मेमोरी में कॉपी किया तो उन्हें या तो एक बफर के लिए मेमोरी आवंटित करनी होगी (इसे नष्ट करने के लिए कॉलर को छोड़ना होगा - एसटीएल अनुबंध का हिस्सा नहीं) या उन्हें आंतरिक स्टैटिक में कॉपी करना होगा बफर (शायद थ्रेडसेफ़ नहीं है, और आपको इसके जीवनकाल की कोई गारंटी नहीं है)। तो बस एक आंतरिक रूप से बनाए रखा अशक्त समाप्त स्ट्रिंग के लिए एक सूचक लौटना ही यथार्थवादी समाधान लगता है।

जवाबों:


9

अगर मुझे याद है, तो मानक string::c_str()बहुत कुछ वापस करने की अनुमति देता है जो संतुष्ट करता है:

  • भंडारण जो स्ट्रिंग की सामग्री और समाप्ति के लिए पर्याप्त है NULL
  • तब तक मान्य होना चाहिए जब तक किसी दिए गए stringऑब्जेक्ट के गैर-कॉन्स्टेबल सदस्य को नहीं बुलाया जाता है

तो व्यवहार में, इसका मतलब आंतरिक भंडारण के लिए एक सूचक है; के रूप में बाह्य रूप से लौटे पॉइंटर के जीवन को ट्रैक करने का कोई तरीका नहीं है। मुझे लगता है कि आपका अनुकूलन यह मानने के लिए सुरक्षित है कि यह (छोटा) निरंतर समय है।

संबंधित नोट पर, यदि स्ट्रिंग स्वरूपण प्रदर्शन सीमित है; आप Boost.Phoenix जैसी किसी चीज़ के साथ पूरी तरह से ज़रूरत होने तक मूल्यांकन को बेहतर बना सकते हैं ।

Boost.Format मेरा मानना ​​है कि जब तक परिणाम की आवश्यकता नहीं होती है, तब तक आंतरिक रूप से फ़ॉर्मेटिंग को समाप्त कर देता है, और आप प्रारूप स्ट्रिंग को फिर से पार्स किए बिना एक ही प्रारूप ऑब्जेक्ट का बार-बार उपयोग कर सकते हैं, जिसे मैंने उच्च आवृत्ति लॉगिंग के लिए एक महत्वपूर्ण अंतर बनाने के लिए पाया है।


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

यदि यह वास्तव में वैध है, तो c_strकॉल वास्तविक समय और नकल के लिए O (n) समय हो सकता है। लेकिन यह भी संभव है कि मानक में अतिरिक्त नियम हैं जो मैं इस बात से अनजान हूं कि इससे बचाव होगा। इसका कारण यह है कि मैं सुझाव देता हूं - कॉल का c_strमतलब वास्तव में आम AFAIK नहीं है, इसलिए यह सुनिश्चित करने के लिए महत्वपूर्ण नहीं माना जा सकता है कि वे तेज हैं - सामान्य रूप से अनावश्यक अशक्त टर्मिनेटर के लिए भंडारण के अतिरिक्त बाइट से बचें stringजो कभी भी उपयोग नहीं c_strकर सकते हैं पूर्वता ले चुके हैं।
स्टीव

Boost.Formatआंतरिक रूप से धाराओं के माध्यम से जाता है जो आंतरिक रूप से sprintfबड़े ओवरहेड के साथ समाप्त हो जाते हैं। प्रलेखन का कहना है कि यह सादे की तुलना में लगभग 8 गुना धीमा है sprintf। यदि आप प्रदर्शन और प्रकार-सुरक्षा चाहते हैं, तो प्रयास करें Boost.Spirit.Karma
Jan Hudec

Boost.Spirit.Karmaप्रदर्शन के लिए एक अच्छा टिप है, लेकिन सावधान रहें कि इसमें एक अलग तरह की कार्यप्रणाली है जो मौजूदा printfशैली कोड (और कोडर्स) को अनुकूलित करने के लिए मुश्किल हो सकती है । मैं काफी हद तक फंस गया Boost.Formatक्योंकि हमारा I / O अतुल्यकालिक है; लेकिन एक बड़ा कारक यह है कि मैं अपने सहकर्मियों को इसे लगातार उपयोग करने के लिए मना सकता हूं (अभी भी किसी भी प्रकार के ostream<<अधिभार के साथ अनुमति देता है - जो अच्छी तरह से .c_str()बहस को आगे बढ़ाता है ) कर्मा सुधार संख्या।
rvalue

23

सी ++ 11 मानक में (मैं एन 3290 संस्करण पढ़ रहा हूं), अध्याय 21.4.7.1 c_str () के बारे में बोलता है:

const charT* c_str() const noexcept; const charT* data() const noexcept;

रिटर्न: एक पॉइंटर पी ऐसा कि p + i == & ऑपरेटर के लिए प्रत्येक i [0, size ()] में।
जटिलता: निरंतर समय।
आवश्यकता है: कार्यक्रम वर्ण सरणी में संग्रहीत किसी भी मान को परिवर्तित नहीं करेगा।

तो, हाँ: मानक द्वारा निरंतर समय जटिलता की गारंटी है।

मैंने अभी c ++ 03 मानक की जाँच की है, और इसकी ऐसी कोई आवश्यकता नहीं है, न ही यह जटिलता बताता है।


8

सिद्धांत में C ++ 03 की आवश्यकता नहीं होती है, और इसलिए स्ट्रिंग चार का एक सरणी हो सकती है जहां null टर्मिनेटर की उपस्थिति को c_str () कहा जाता है। इसके लिए एक वास्तविक आवंटन की आवश्यकता होती है (यह आंतरिक निजी सूचक के रूप में घोषित किए जाने पर, कॉन्स्ट-नेस का उल्लंघन नहीं करता है mutable)।

सी ++ 11 सख्त है: इसमें समय की आवश्यकता होती है, इसलिए कोई स्थानांतरण नहीं किया जा सकता है और नल को अंत में स्टोर करने के लिए सरणी को हमेशा चौड़ा होना चाहिए। c_str (), अपने आप से, अभी भी कर सकते हैं " ptr[size()]='\0'" यह सुनिश्चित करने के लिए कि नल वास्तव में मौजूद है। यह सरणी के const-ness का उल्लंघन नहीं करता है क्योंकि सीमा [0..size())परिवर्तित नहीं होती है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.