2011-02-04 15 views
7

इस कोड टुकड़ा को ध्यान में रखते:printf को CSTring के वर्ण डेटा के पते को कैसे पता है?

struct My { 
    operator const char*()const{ return "my"; } 
} my; 

CStringA s("aha"); 
printf("%s %s", s, my); 


// another variadic function to get rid of comments about printf :) 
void foo(int i, ...) { 
    va_list vars; 
    va_start(vars, i); 
    for(const char* p = va_arg(vars,const char*) 
    ; p != NULL 
    ; p=va_arg(vars,const char*)) 
    { 
    std::cout << p << std::endl; 
    } 
    va_end(vars); 
} 
foo(1, s, my); 

'सहज' उत्पादन 'अहा "में यह स्निपेट का परिणाम है। लेकिन मैं एक सुराग कि यह कैसे काम कर सकते हैं नहीं मिला है:

  • अगर variadic-समारोह कॉल तर्कों के संकेत दिए गए धकेलने में अनुवादित किया गया printf एक CStringA* कि एक const char*
  • अगर के रूप में व्याख्या की है प्राप्त होगा वैरिएडिक-फ़ंक्शन कॉल operator (const char*) पर कॉल कर रहा है, यह मेरी अपनी कक्षा के लिए ऐसा क्यों नहीं करेगा?

क्या कोई इसे समझा सकता है?

संपादित करें: एक डमी वैरैडिक फ़ंक्शन जोड़ा गया है जो इसके तर्कों को const char* एस के रूप में मानता है। देखें - यह my तर्क तक पहुंचने पर भी दुर्घटनाग्रस्त हो जाता है ...

+5

प्रिंटफ और सी ++, टट टट –

+0

@ डेविड हेफ़ेमन: मुझे पता है। 'printf' मेरा प्रोटोटाइप वैरैडिक फ़ंक्शन है। वास्तविक कोड जो मैं संदर्भित करता हूं वह एक 'सीएसटींग :: प्रारूप' है। – xtofl

+0

यह क्या मंच है? विजुअल स्टूडियो कोई रूपांतरण नहीं करता है, यह सिर्फ स्टैक पर पूरे मान को धक्का देता है। जीसीसी एक जोर देते हैं। मुझे लगता है कि आप बस "भाग्यशाली" हैं सीएसटींग का पहला सदस्य डेटा के लिए एक सूचक है। – Suma

उत्तर

7

सी ++ 98 मानक के प्रासंगिक पाठ §5.2.2/7:

lvalue करने वाली rvalue (4.1), सरणी-टू-सूचक (4.2), और समारोह-हैं- पॉइंटर (4.3) मानक रूपांतरण तर्क अभिव्यक्ति पर किया जाता है। इन रूपांतरणों के बाद, यदि तर्क में अंकगणित, गणना, सूचक, सदस्य के लिए सूचक, या वर्ग प्रकार नहीं है, तो कार्यक्रम खराब हो गया है। यदि तर्क में गैर-पीओडी वर्ग प्रकार (खंड 9) है, तो व्यवहार अपरिभाषित है।

तो औपचारिक रूप से व्यवहार अपरिभाषित है।

हालांकि, एक दिया गया कंपाइलर भाषा एक्सटेंशन की संख्या प्रदान कर सकता है, और विजुअल सी ++ करता है। MSDN Library... करने के लिए, इस प्रकार विज़ुअल सी की ++ व्यवहार दस्तावेजों गुजर तर्क के संबंध में:

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

यह नहीं करता है ’ टी विज़ुअल सी के बारे में ++ उपयोगकर्ता निर्धारित रूपांतरण का आवेदन करने से कुछ भी उल्लेख करते हैं।

यह CString::Format हो सकता है, जो कि आप वास्तव में रुचि रखते हैं कि ’ फ़ंक्शन है, ऊपर दिए गए अंतिम बिंदु पर निर्भर करता है।

चीयर्स & hth।,

6

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

+1

वह 'सीएसटींग * * नहीं बल्कि एक' सीएसटींगिंग 'पास नहीं कर रहा है, जो' ऑपरेटर ओवरलोड करता है (एलपीसीटीआरटी ''। – Benoit

+0

काफी सही, मुझे याद आया। उस स्थिति में, यह एक गैर मानक विस्तार है। –

+0

यह एक गैर-मानक विस्तार क्यों है? –

1

यदि भिन्नता-फ़ंक्शन कॉल ऑपरेटर (कॉन्स्ट char *) को कॉल कर रहा है, तो यह मेरी कक्षा के लिए ऐसा क्यों नहीं करेगा?

हाँ, लेकिन आपको इसे स्पष्ट रूप से अपने कोड में डालना चाहिए: printf("%s", (LPCSTR)s, ...);

0

आपका printf बयान गलत है:

printf("%s", s, my); 

होना चाहिए:

printf("%s %s", s, my); 

कौन सा बाहर प्रिंट होगा "अहा मेरी"। (इसके वास्तव में LPCTSTR जो एक const TCHAR* है के लिए - CStringALPCSTR के लिए एक रूपांतरण समारोह है)

cstring const char* के लिए एक converstion ऑपरेटर है।

printf कॉल CStringA ऑब्जेक्ट को CStringA* पॉइंटर में परिवर्तित नहीं करेगा। यह अनिवार्य रूप से इसे void* जैसा व्यवहार करता है। सीएसटींग के मामले में, यह बहुत ही भाग्यशाली है (या शायद माइक्रोसॉफ्ट के डेवलपर्स का डिज़ाइन जो मानक में नहीं है) का लाभ ले रहा है कि यह आपको न्यूल-टर्मिनेटेड स्ट्रिंग देगा। यदि आप रूपांतरण फ़ंक्शन होने के बावजूद _bstr_t (जिसके पहले स्ट्रिंग का आकार पहले है) का उपयोग करना था, तो यह बहुत ही असफल हो जाएगा।

यह printf (या उस मामले के लिए कोई भिन्न कार्य) कहने पर आप अपनी ऑब्जेक्ट्स/पॉइंटर्स को स्पष्ट रूप से डालने के लिए अच्छा अभ्यास (और कई मामलों में आवश्यक) है।

+0

वह स्पष्ट रूप से 'CStringA' का उपयोग कर रहा है, इसलिए वह' (एलपीसीआरटी) 'चाहता है। वास्तव में – Benoit

+0

। मैंने बिंदु को चित्रित करने के लिए बाद में 'my' जोड़ा। मैं इस प्रकार सवाल को संपादित करूंगा। – xtofl

+0

यह "इसे उपयोगी चीज़ों में कैसे डालेगा"?इसकी अनुमति कहाँ है? –

4

आप गैर-पीओडी डेटा को विविध कार्यों में सम्मिलित नहीं कर सकते हैं। More info

+1

आप कर सकते हैं, लेकिन यह अपरिभाषित व्यवहार है। – sharptooth

+0

और कैसे 'माई' पीओडी नहीं है? ... – xtofl

+0

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

1

variadic-समारोह कॉल तर्कों के संकेत दिए गए धकेलने में अनुवाद किया गया है, तो ...

है कि नहीं कैसे variadic कार्यों काम करते हैं। तर्कों के पॉइंटर्स के बजाए तर्कों के मूल्यों को अंतर्निहित प्रकारों (जैसे कि char to int) के विशेष रूपांतरण नियमों के बाद पारित किया जाता है।

सी ++ 03 §5.2.2p7:

जब किसी दिए गए तर्क, तर्क इस तरह से कि प्राप्त समारोह लागू द्वारा तर्क का मान प्राप्त कर सकते हैं में पारित हो जाता है के लिए कोई पैरामीटर है va_arg (18.7)। लवली-टू-रावल्यू (4.1), सरणी-टू-पॉइंटर (4.2), और फ़ंक्शन-टू-पॉइंटर (4.3) मानक रूपांतरण तर्क अभिव्यक्ति पर किए जाते हैं। इन रूपांतरणों के बाद, यदि तर्क में अंकगणित, गणना, सूचक, सदस्य के लिए सूचक, या वर्ग प्रकार नहीं है, तो कार्यक्रम खराब हो गया है। यदि तर्क में गैर-पीओडी वर्ग प्रकार (खंड 9) है, तो व्यवहार अपरिभाषित है। यदि तर्क में अभिन्न या गणना प्रकार है जो अभिन्न प्रचार (4.5) के अधीन है, या एक फ़्लोटिंग पॉइंट प्रकार जो फ़्लोटिंग पॉइंट प्रोमोशन (4.6) के अधीन है, तो तर्क का मान कॉल से पहले प्रचारित प्रकार में परिवर्तित हो जाता है । इन प्रचारों को डिफ़ॉल्ट तर्क प्रचार के रूप में जाना जाता है।

विशेष रूप से ऊपर से:

तर्क एक गैर पॉड वर्ग प्रकार (खंड 9) है, तो व्यवहार अनिर्धारित रहता है।

सी ++ va_arg की परिभाषा के लिए सेल्सियस तक पंट, और C99 TC3 §7.15.1.2p2 का कहना है:

... अगर प्रकार वास्तविक अगले तर्क के प्रकार के साथ संगत नहीं है (जैसा कि अनुसार पदोन्नत डिफ़ॉल्ट तर्क प्रचारों के लिए), निम्नलिखित मामलों को छोड़कर व्यवहार अपरिभाषित है: [उन मामलों की सूची जो यहां लागू नहीं होती हैं]

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

प्रिंटफ किसी भी उपयोगकर्ता द्वारा परिभाषित कक्षा प्रकार के लिए सही प्रकार लागू नहीं करेगा क्योंकि इसमें उनके बारे में कोई जानकारी नहीं है, इसलिए आप किसी भी यूडीटी कक्षा प्रकार को printf में पास नहीं कर सकते हैं। आपका foo सही वर्ग प्रकार के बजाय चार पॉइंटर का उपयोग करके वही काम करता है।

+2

"सबसे बुरे मामले में, यह ठीक वैसे ही काम करता है जैसा आप उम्मीद करते हैं" ... मुझे वह पसंद है! – xtofl

1

यह नहीं करता है। यह operator const char* भी कॉल नहीं करता है। विज़ुअल सी ++ बस क्लास डेटा को printf पर पास करता है जैसे कि memcpy। यह CString वर्ग के लेआउट के कारण काम करता है: इसमें केवल एक सदस्य चर होता है जो वर्ण डेटा के लिए सूचक होता है।