प्रिंटफ - बग का स्रोत? [बन्द है]


9

मैं printfअपने कोड में बहुत सारे ट्रेसिंग / लॉगिंग उद्देश्यों के लिए उपयोग कर रहा हूं , मैंने पाया है कि यह प्रोग्रामिंग त्रुटि का स्रोत है। मुझे हमेशा सम्मिलन ऑपरेटर ( <<) कुछ हद तक अजीब लगता है, लेकिन मुझे लगता है कि इसके बजाय मैं इसका उपयोग करके इन कुछ कीड़ों से बच सकता हूं।

किसी को कभी भी एक समान रहस्योद्घाटन किया गया था या मैं यहाँ तिनके पर बस कर रहा हूँ?

कुछ दूर अंक ले

  • मेरी सोचने की वर्तमान पंक्ति यह है कि टाइप-सेफ्टी ने प्रिंटफ का उपयोग करने के किसी भी लाभ को आगे बढ़ाया। असली समस्या प्रारूप स्ट्रिंग और गैर-प्रकार-सुरक्षित वैरेडिक कार्यों का उपयोग है।
  • शायद मैं उपयोग नहीं कर रहा हूँ <<और stl आउटपुट स्ट्रीम वेरिएंट लेकिन मैं निश्चित रूप से एक प्रकार-सुरक्षित तंत्र का उपयोग करूंगा जो बहुत समान है।
  • बहुत से अनुरेखण / लॉगिंग सशर्त है, लेकिन मैं हमेशा परीक्षणों में बग को याद न करने के लिए कोड चलाना चाहता हूं क्योंकि यह शायद ही कभी शाखा है।

4
printfसी ++ दुनिया में? मुझे यहाँ कुछ याद आ रहा है?
user827992

10
@ user827992: क्या आप इस तथ्य को याद कर रहे हैं कि C ++ मानक में संदर्भ द्वारा C मानक पुस्तकालय शामिल है? यह printfC ++ में उपयोग करने के लिए पूरी तरह से कानूनी है । (क्या यह एक अच्छा विचार है एक और सवाल है।)
कीथ थॉम्पसन

2
@ user827992: printfकुछ फायदे हैं; मेरा जवाब देखिए।
कीथ थॉम्पसन

1
यह प्रश्न बहुत सीमावर्ती है। "आप लोग क्या सोचते हैं" सवाल अक्सर बंद होते हैं।
dbracey

1
@vitaut मुझे लगता है (टिप के लिए धन्यवाद)। मैं आक्रामक संयम से थोड़ा हैरान हूं। यह वास्तव में प्रोग्रामिंग स्थितियों के बारे में दिलचस्प चर्चाओं को बढ़ावा नहीं देता है जो कि मैं और अधिक करना चाहता हूं।
बजे जॉन लीडग्रेन

जवाबों:


2

प्रिंटफ, विशेष रूप से ऐसे मामलों में जहां आप प्रदर्शन के बारे में परवाह कर सकते हैं (जैसे स्प्रिंटफ और फ़र्फ़रफ़) वास्तव में अजीब हैक है। यह मुझे लगातार आश्चर्यचकित करता है कि जो लोग वर्चुअल कार्यों से संबंधित ओवरहेड प्रदर्शन के कारण C ++ पर पाउंड करते हैं, वे फिर C के io की रक्षा के लिए आगे बढ़ेंगे।

हां, हमारे आउटपुट के प्रारूप का पता लगाने के लिए, कुछ ऐसा जिसे हम संकलन समय पर 100% जान सकते हैं, आइए एक अचूक प्रारूप स्ट्रिंग का उपयोग करके एक बड़े पैमाने पर अजीब कूद तालिका के अंदर रनटाइम पर एक फ्रेंकेन प्रारूप स्ट्रिंग को पार्स करें!

बेशक ये प्रारूप कोड उन प्रकारों से मेल खाने के लिए नहीं बनाए जा सकते हैं, जिनका वे प्रतिनिधित्व करते हैं, जो बहुत आसान होगा ... और आपको हर बार यह देखने के लिए याद दिलाया जाता है कि क्या यह% llg या% lg है कि यह (दृढ़ता से टाइप की गई) भाषा आपको बनाती है कुछ प्रिंट करने / स्कैन करने के लिए मैन्युअल रूप से टाइप करें, और पूर्व -32 बिट प्रोसेसर के लिए डिज़ाइन किया गया था।

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

(यदि आप मानक कंटेनरों का उपयोग करते हैं, तो अपने आप को कुछ त्वरित टेम्प्लेटेड ऑपरेटर std::cout << my_list << "\n";लिखें list<vector<pair<int,string> > >। ओवरलोड्स जो आपको डिबग जैसी चीजों को करने की अनुमति देते हैं , जहां my_list प्रकार का है ।)


1
मानक C ++ लाइब्रेरी की समस्या यह है कि ज्यादातर अवतार operator<<(ostream&, T)कॉल करके लागू होते हैं ... ठीक है sprintf,! का प्रदर्शन sprintfइष्टतम नहीं है, लेकिन इसके कारण, iostreams का प्रदर्शन आमतौर पर और भी बदतर है।
Jan Hudec

@ जानहुडेक: इस बिंदु पर लगभग एक दशक तक सही नहीं रहा। वास्तविक मुद्रण एक ही अंतर्निहित सिस्टम कॉल के साथ किया जाता है, और C ++ कार्यान्वयन अक्सर इसके लिए C पुस्तकालयों में कॉल करते हैं ... लेकिन प्रिंटिंग के माध्यम से std :: cout रूटिंग के समान नहीं है।
जुकरियाई

16

C ++ स्टाइल आउटपुट के साथ C- स्टाइल printf()(या puts()या putchar()...) आउटपुट को std::cout << ...मिलाया जाना असुरक्षित हो सकता है। यदि मैं सही ढंग से याद करता हूं, तो उनके पास अलग-अलग बफरिंग तंत्र हो सकते हैं, इसलिए आउटपुट अपेक्षित क्रम में प्रकट नहीं हो सकता है। (जैसा कि एपीग्रामग्राम एक टिप्पणी में उल्लेख करता है, इसे sync_with_stdioसंबोधित करता है)।

printf()मौलिक रूप से असुरक्षित है। प्रकार एक बहस के लिए उम्मीद प्रारूप स्ट्रिंग से निर्धारित होता है ( "%d"एक की आवश्यकता है intया कुछ है कि को बढ़ावा देता है int, "%s"एक की आवश्यकता है char*जो एक सही ढंग से समाप्त सी शैली स्ट्रिंग, आदि को इंगित करना होगा), लेकिन अपरिभाषित व्यवहार में तर्क परिणामों की गलत प्रकार गुजर , एक नैदानिक ​​त्रुटि नहीं। कुछ संकलक, जैसे कि जीसीसी, टाइप मिसमैच के बारे में चेतावनी का एक बहुत अच्छा काम करते हैं, लेकिन वे ऐसा केवल तभी कर सकते हैं जब प्रारूप स्ट्रिंग शाब्दिक है या अन्यथा संकलन के समय (जो सबसे आम मामला है) - और जैसे भाषा द्वारा चेतावनियों की आवश्यकता नहीं है। यदि आप गलत प्रकार के तर्क को पारित करते हैं, तो मनमाने ढंग से खराब चीजें हो सकती हैं।

सी ++ की धारा I / O, दूसरी ओर, बहुत अधिक प्रकार-सुरक्षित है, क्योंकि <<ऑपरेटर कई अलग-अलग प्रकारों के लिए अतिभारित है। std::cout << xके प्रकार को निर्दिष्ट करने की आवश्यकता नहीं है x; संकलक जो भी प्रकार के लिए सही कोड उत्पन्न करेगा x

दूसरी ओर, printfप्रारूपण विकल्प IMHO अधिक सुविधाजनक हैं। यदि मैं दशमलव बिंदु के बाद 3 अंकों के साथ एक फ्लोटिंग-पॉइंट वैल्यू प्रिंट करना चाहता हूं, तो मैं उपयोग कर सकता हूं "%.3f"- और इसका एक ही printfकॉल के भीतर भी अन्य तर्कों पर कोई प्रभाव नहीं पड़ता है । setprecisionदूसरी ओर, C ++ की धारा की स्थिति को प्रभावित करता है, और बाद के आउटपुट को गड़बड़ कर सकता है यदि आप स्ट्रीम को उसकी पिछली स्थिति में पुनर्स्थापित करने के लिए बहुत सावधान नहीं हैं। (यह मेरा निजी पालतू जानवर है; अगर मुझे इससे बचने के लिए कोई साफ रास्ता याद आ रहा है, तो कृपया टिप्पणी करें।)

दोनों के फायदे और नुकसान हैं। printfयदि आप C बैकग्राउंड वाले हैं और आप इससे अधिक परिचित हैं, या यदि आप C सोर्स प्रोग्राम को C ++ प्रोग्राम में आयात कर रहे हैं, तो यह उपलब्धता विशेष रूप से उपयोगी है। std::cout << ...C ++ के लिए अधिक मुहावरेदार है, और प्रकार के बेमेल से बचने के लिए उतनी देखभाल की आवश्यकता नहीं है। दोनों वैध हैं सी ++ (सी ++ मानक में संदर्भ द्वारा सी मानक पुस्तकालय के अधिकांश शामिल हैं)।

यह शायद का उपयोग करने के लिए सबसे अच्छा std::cout << ...अन्य सी ++ प्रोग्रामर जो अपने कोड पर काम कर सकते हैं के लिए है, लेकिन आप या तो एक का उपयोग कर सकते हैं - विशेष रूप से पता लगाने कोड में है कि तुम फेंक जा रहे हैं।

और निश्चित रूप से यह डिबगर्स का उपयोग करना सीखने के लिए कुछ समय बिताने के लायक है (लेकिन यह कुछ वातावरण में संभव नहीं है)।


मूल प्रश्न में मिश्रण का कोई उल्लेख नहीं है।
dbracey

1
@dbracey: नहीं, लेकिन मुझे लगा कि यह एक संभावित कमी के रूप में उल्लेख करने योग्य है printf
कीथ थॉम्पसन

6
सिंक्रनाइज़ेशन समस्या के लिए, देखें std::ios_base::sync_with_stdio
एपीग्रामग्राम

1
+1 std :: एक मल्टीथ्रेड एप्लिकेशन में डीबग जानकारी प्रिंट करने के लिए cout का उपयोग करना 100% बेकार है। कम से कम प्रिंटफ चीजों के साथ आदमी या मशीन द्वारा दखल और अनपेक्षित होने की संभावना नहीं है।
जेम्स

@ नाम: ऐसा इसलिए है क्योंकि std::coutप्रत्येक आइटम जो मुद्रित है के लिए एक अलग कॉल का उपयोग करता है? आप इसे प्रिंट करने से पहले एक स्ट्रिंग में आउटपुट की एक लाइन एकत्र करके चारों ओर काम कर सकते हैं। और निश्चित रूप से आप एक बार में एक आइटम भी प्रिंट कर सकते हैं printf; एक कॉल में एक पंक्ति (या अधिक) मुद्रित करना अधिक सुविधाजनक है।
कीथ थॉम्पसन

2

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

printf("%d", SomeObject);

जबकि <<नहीं होगा।

नोट: डीबगिंग के लिए, आप उपयोग printfया नहीं करते हैं cout। आप का उपयोग fprintf(stderr, ...)और cerr


मूल प्रश्न में मिश्रण का कोई उल्लेख नहीं है।
dbracey

बेशक आप किसी वस्तु के पते को प्रिंट कर सकते हैं लेकिन बड़ा अंतर यह है कि printfटाइप-सेफ नहीं है और मैं सोच की वर्तमान लाइन है कि टाइप-सेफ्टी का उपयोग करने के किसी भी लाभ से अधिक है printf। समस्या वास्तव में प्रारूप स्ट्रिंग और प्रकार-सुरक्षित वैरेडिक फ़ंक्शन नहीं है।
जॉन लीडग्रेन

@ जॉनीलाइडग्रेन: लेकिन क्या होगा अगर SomeObjectकोई पॉइंटर न हो? YOur को मनमाने ढंग से बाइनरी डेटा मिलने वाला है जो कंपाइलर तय करता है SomeObject
Linux

मुझे लगता है कि मैं आपके उत्तर को पीछे की ओर पढ़ता हूं ... एनवीएम।
जॉन लेडिग्रेन

1

कई समूह हैं - उदाहरण के लिए google - जो स्ट्रीम पसंद नहीं करते हैं।

http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Streams

(पॉप त्रिभुज बात खोलें ताकि आप चर्चा देख सकें।) मुझे लगता है कि Google C ++ शैली गाइड में बहुत समझदार सलाह है।

मुझे लगता है कि ट्रेडऑफ़ यह है कि स्ट्रीम सुरक्षित हैं लेकिन प्रिंटफ़ पढ़ने के लिए स्पष्ट है (और वास्तव में आप चाहते हैं कि स्वरूपण प्राप्त करना आसान है)।


2
Google शैली मार्गदर्शिका अच्छी है, लेकिन इसमें बहुत कुछ आइटम शामिल हैं जो सामान्य उद्देश्य गाइड के लिए उपयुक्त नहीं हैं । (जो ठीक है, क्योंकि आखिरकार यह Google के लिए / पर चलने वाले कोड के लिए Google का मार्गदर्शक है ।)
मार्टिन बा

1

printfप्रकार की सुरक्षा की कमी के कारण कीड़े हो सकते हैं। वहाँ को संबोधित है कि पर स्विच किए बिना की कुछ तरीके हैं iostreamके <<ऑपरेटर और अधिक स्वरूपण जटिल:

  • कुछ संकलक (जैसे कि जीसीसी और क्लैंग) वैकल्पिक रूप से आपके printfप्रारूप को printfतर्कों के खिलाफ जांच सकते हैं और यदि वे मेल नहीं खाते हैं तो निम्नलिखित जैसे चेतावनी प्रदर्शित कर सकते हैं।
    चेतावनी: रूपांतरण प्रकार 'int' निर्दिष्ट करता है लेकिन तर्क में 'char *' टाइप है
  • Typeafeprintf स्क्रिप्ट आपके -स्टाइलprintf कॉल को प्री -टाइप कर सकती है ताकि उन्हें टाइप-सुरक्षित बनाया जा सके।
  • Boost.Format और FastFormat जैसी लाइब्रेरी आपको 'टाइप सुरक्षा और टाइप एक्सटेंसीबिलिटी रखते हुए printf(जैसे विशेष रूप से Boost.Format का विशेष रूप से लगभग समान printf) का उपयोग करने देती हैं iostreams

1

Printf सिंटैक्स मूल रूप से ठीक है, कुछ अस्पष्ट टाइपिंग के शून्य से। अगर आपको लगता है कि यह गलत है तो C #, पायथन और अन्य भाषाएं समान निर्माण का उपयोग क्यों करती हैं? C या C ++ में समस्या: यह एक भाषा का हिस्सा नहीं है और इस प्रकार सही सिंटैक्स (*) के लिए संकलक द्वारा जांच नहीं की जाती है और गति के लिए अनुकूलन करते हुए देशी कॉल की श्रृंखला में विघटित नहीं होती है। ध्यान दें कि यदि आकार के लिए अनुकूलन, प्रिंटफ कॉल अधिक कुशल हो सकता है! C ++ स्ट्रीमिंग सिंटैक्स imho कुछ भी लेकिन अच्छा है। यह काम करता है, टाइप-सेफ्टी है, लेकिन वर्बोस सिंटैक्स ... ब्लोह। मेरा मतलब है कि मैं इसका इस्तेमाल करता हूं, लेकिन बिना खुशी के।

(*) कुछ कंपाइलर इस चेकिंग प्लस लगभग सभी स्टैटिक एनालिसिस टूल्स करते हैं (मैं लिंट का उपयोग करता हूं और कभी भी प्रिंटफ के साथ कोई समस्या नहीं थी)।


1
नहीं है Boost.Format कि सुविधाजनक वाक्य रचना (जोड़ती format("fmt") % arg1 % arg2 ...;प्रकार-सुरक्षा के साथ)। कुछ और प्रदर्शन की कीमत पर, क्योंकि यह स्ट्रिंगस्ट्रीम कॉल उत्पन्न करता है जो आंतरिक रूप से कई कार्यान्वयन में स्प्रिंटफ कॉल उत्पन्न करता है।
Jan Hudec

0

printfमेरी राय में, सीपीपी स्ट्रीम आउटपुट के किसी भी संस्करण की तुलना में चर से निपटने के लिए कहीं अधिक लचीला आउटपुट टूल है। उदाहरण के लिए:

printf ( "%d in ANSI = %c\n", j, j ); /* Perfectly valid... if a char ISN'T printing right, I'd just check the integer value to make sure it was okay. */

हालाँकि, जहाँ आप CPP <<ऑपरेटर का उपयोग करना चाहते हैं, जब आप इसे किसी विशेष विधि के लिए अधिभारित करते हैं ... उदाहरण के लिए किसी विशेष व्यक्ति के डेटा को रखने वाली किसी वस्तु का डंप प्राप्त करना PersonData...।

ostream &operator<<(ostream &stream, PersonData obj)
{
 stream << "\nName: " << name << endl;
 stream << " Number: " << phoneNumber << endl;
 stream << " Age: " << age << endl;
 return stream;
}

उसके लिए, यह कहना अधिक प्रभावी होगा (मान aलेना व्यक्तित्व की वस्तु है)

std::cout << a;

से:

printf ( "Name: %s\n Number: %s\n Age: %d\n", a.name, a.number, a.age );

पूर्व में एन्कैप्सुलेशन के सिद्धांत के साथ इन-लाइन बहुत अधिक है (विशिष्टताओं को जानने की आवश्यकता नहीं है, निजी सदस्य चर), और पढ़ना भी आसान है।


0

आप printfC ++ में उपयोग करने वाले नहीं हैं । कभी। इसका कारण यह है, जैसा कि आपने सही ढंग से उल्लेख किया है, कि यह बग का एक स्रोत है और यह तथ्य कि कस्टम प्रकार को मुद्रित करना, और सी ++ में लगभग सब कुछ कस्टम प्रकार होना चाहिए, दर्द है। सी ++ समाधान धाराओं है।

हालांकि एक महत्वपूर्ण समस्या है जो किसी भी और उपयोगकर्ता-दृश्यमान आउटपुट के लिए स्ट्रीम को अनुपयुक्त बना देती है! समस्या अनुवाद है। गेटटेक्स्ट मैनुअल से उधार उदाहरण आप लिखना चाहते हैं:

cout << "String '" << str << "' has " << str.size() << " characters\n";

अब जर्मन अनुवादक साथ आता है और कहता है: ठीक है, जर्मन में, संदेश होना चाहिए

एन ज़ीचेन लैंग इस्ट डाई डाई ज़ीचेंकेट ' s '

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

printf("%2$d Zeichen lang ist die Zeichenkette '%1$s'", ...);

Boost.Format का समर्थन करता है printf शैली स्वरूपों और इस सुविधा है। तो आप लिखते हैं:

cout << format("String '%1' has %2 characters\n") % str % str.size();

दुर्भाग्य से यह थोड़ा सा निष्पादन दंड देता है, क्योंकि आंतरिक रूप से यह एक स्ट्रीस्टस्ट बनाता है और <<प्रत्येक बिट को प्रारूपित करने के लिए ऑपरेटर का उपयोग करता है और कई कार्यान्वयन में <<ऑपरेटर आंतरिक कॉल करता है sprintf। मुझे संदेह है कि यदि वास्तव में वांछित है तो अधिक कुशल कार्यान्वयन संभव होगा।


-1

आप बहुत सारे बेकार काम कर रहे हैं, इस तथ्य के साथ कि stlयह बुराई है या नहीं, अपने कोड को डीबग करें printfकेवल संभव विफलताओं का 1 और स्तर जोड़ें।

बस डिबगर का उपयोग करें और अपवादों के बारे में कुछ पढ़ें और उन्हें कैसे पकड़ें और फेंक दें; आप वास्तव में होने की जरूरत से ज्यादा क्रियात्मक नहीं होने का प्रयास करें।

पुनश्च

printf C में उपयोग किया जाता है, C ++ के लिए आपके पास है std::cout


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