प्रिंट के साथ std :: स्ट्रिंग?


157

मेरी समझ यह है कि नाम स्थान stringका एक सदस्य है std, तो निम्न क्यों होता है?

#include <iostream>

int main()
{
    using namespace std;

    string myString = "Press ENTER to quit program!";
    cout << "Come up and C++ me some time." << endl;
    printf("Follow this command: %s", myString);
    cin.get();

    return 0;
}

यहां छवि विवरण दर्ज करें

हर बार जब प्रोग्राम चलता है, तो myStringऊपर के आउटपुट में 3 वर्णों का प्रतीत होने वाला यादृच्छिक स्ट्रिंग प्रिंट करता है।


8
बस आपको बता दें, बहुत सारे लोग उस किताब की आलोचना करते हैं। जिसे मैं समझ सकता हूं, क्योंकि ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग के बारे में बहुत कुछ नहीं है, लेकिन मुझे नहीं लगता कि यह उतना बुरा है जितना लोग दावा करते हैं।
जेसी गुड

ouf! ठीक है, यह अच्छा है जब मैं किताब के माध्यम से अपना रास्ता बनाऊं, इसे ध्यान में रखना। मुझे यकीन है कि यह केवल सी ++ पुस्तक नहीं होगी जिसे मैं अगले साल या इसके बाद पढ़ूंगा, इसलिए मुझे आशा है कि यह बहुत ज्यादा नुकसान नहीं करेगा :)
TheDarkIn1978

उच्चतम संकलक चेतावनी का उपयोग करना आपके प्रश्न का उत्तर देगा - जब gcc के साथ संकलित करना। MSVC इसे कैसे संभालता है - मुझे नहीं पता।
पीटर वर्गा

जवाबों:


237

यह संकलित है क्योंकि printfयह सुरक्षित नहीं है, क्योंकि यह C अर्थ 1 में परिवर्तनशील तर्कों का उपयोग करता है । केवल सी-स्टाइल स्ट्रिंग के printfलिए कोई विकल्प नहीं है std::string। इसकी अपेक्षा में कुछ और का उपयोग करना निश्चित रूप से आपको इच्छित परिणाम नहीं देगा। यह वास्तव में अपरिभाषित व्यवहार है, इसलिए कुछ भी हो सकता है।

इसे ठीक करने का सबसे आसान तरीका, जब से आप C ++ का उपयोग कर रहे हैं, इसे सामान्य रूप से प्रिंट कर std::coutरहा std::stringहै , क्योंकि ऑपरेटर ओवरलोडिंग के माध्यम से इसका समर्थन करता है:

std::cout << "Follow this command: " << myString;

यदि, किसी कारण से, आपको सी-स्टाइल स्ट्रिंग निकालने की आवश्यकता है, तो आप इसे शून्य-समाप्त करने की c_str()विधि का उपयोग कर सकते हैं । अपने उदाहरण का उपयोग करना:std::stringconst char *

#include <iostream>
#include <string>
#include <stdio.h>

int main()
{
    using namespace std;

    string myString = "Press ENTER to quit program!";
    cout << "Come up and C++ me some time." << endl;
    printf("Follow this command: %s", myString.c_str()); //note the use of c_str
    cin.get();

    return 0;
}

यदि आप एक ऐसा फंक्शन चाहते हैं printf, जो सुरक्षित हो, लेकिन सुरक्षित हो, तो वैरेडिक टेम्प्लेट (C ++ 11, MSVC12 के रूप में सभी प्रमुख कंपाइलरों पर समर्थित) देखें। आप यहां एक उदाहरण पा सकते हैं । ऐसा कुछ भी नहीं है जिसे मैं मानक पुस्तकालय में लागू करने के बारे में जानता हूं, लेकिन विशेष रूप से बूस्ट में हो सकता है boost::format


[१]: इसका मतलब यह है कि आप किसी भी तर्क को पारित कर सकते हैं, लेकिन फ़ंक्शन आपको उन तर्कों की संख्या और प्रकार बताने के लिए निर्भर करता है। के मामले में printf, जिसका अर्थ है एन्कोडेड प्रकार की जानकारी के साथ एक स्ट्रिंग %dजिसका अर्थ है int। यदि आप प्रकार या संख्या के बारे में झूठ बोलते हैं, तो फ़ंक्शन के पास जानने का कोई मानक तरीका नहीं है, हालांकि कुछ कंपाइलरों में झूठ बोलने पर चेकों की जांच करने और चेतावनी देने की क्षमता होती है।


@MingDuck, अच्छी बात है। यह जेरी के उत्तर में है, लेकिन स्वीकृत उत्तर होने के नाते, यह वही है जो लोग देखते हैं, और वे दूसरों को देखने से पहले छोड़ सकते हैं। मैंने उस विकल्प को जोड़ा है ताकि पहले समाधान को देखा जा सके, और अनुशंसित को।
क्रिस

43

कृपया उपयोग न करें printf("%s", your_string.c_str());

cout << your_string;इसके बजाय उपयोग करें । लघु, सरल और प्रकार। वास्तव में, जब आप C ++ लिख रहे होते हैं, तो आप आम तौर पर printfपूरी तरह से बचना चाहते हैं - यह C से एक ऐसा बदलाव है जो C ++ में शायद ही कभी आवश्यक या उपयोगी हो।

जैसा कि आप coutइसके बजाय क्यों उपयोग करना चाहिए printf, कारण कई हैं। यहाँ सबसे स्पष्ट में से कुछ का नमूना है:

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

    std::locale loc("");
    std::cout.imbue(loc);
    
    std::cout << 123456.78;
    

    अनाम लोकेल ("") उपयोगकर्ता के कॉन्फ़िगरेशन के आधार पर एक लोकेल चुनती है। इसलिए, मेरी मशीन पर (यूएस अंग्रेजी के लिए कॉन्फ़िगर किया गया) यह प्रिंट करता है 123,456.78। किसी ऐसे व्यक्ति के लिए जिसका कंप्यूटर जर्मनी के लिए कॉन्फ़िगर किया गया है (कहते हैं), यह कुछ इस तरह प्रिंट करेगा 123.456,78। भारत के लिए कॉन्फ़िगर किए गए किसी व्यक्ति के लिए, यह प्रिंट आउट होगा 1,23,456.78(और निश्चित रूप से कई अन्य हैं)। printfमैं बिल्कुल एक परिणाम प्राप्त करने के साथ 123456.78:। यह सुसंगत है, लेकिन यह हर जगह हर किसी के लिए लगातार गलत है। अनिवार्य रूप से इसके चारों ओर काम करने का एकमात्र तरीका अलग से फ़ॉर्मेटिंग करना है, फिर परिणाम को स्ट्रिंग के रूप में पास करें printf, क्योंकि printfस्वयं केवल सही ढंग से काम नहीं करेगा ।

  4. हालांकि वे काफी कॉम्पैक्ट हैं, printfस्वरूपण काफी अपठनीय हो सकते हैं। यहां तक कि सी प्रोग्रामर का उपयोग करने वाले के बीच में printfहर दिन लगभग, मुझे लगता है था कम से कम 99% चीजों को देखने के लिए क्या सुनिश्चित करने के लिए की आवश्यकता होगी #में %#xइसका मतलब है, और कैसे क्या से है कि अलग है #में %#fसाधन (और हाँ, वे पूरी तरह से अलग अलग बातें मतलब )।

11
@ TheDarkIn1978: आप शायद भूल गए #include <string>। VC ++ के हेडर में कुछ विषमताएं हैं जो आपको एक स्ट्रिंग को परिभाषित करने देंगी, लेकिन हेडर coutको शामिल किए बिना इसे नहीं भेजें <string>
जेरी कॉफिन

28
@ जेरी: बस यह बताना चाहते हैं कि बड़े डेटा से निपटने के दौरान प्रिंट का उपयोग करने की तुलना में प्रिंटफ़ का उपयोग करना अधिक तेज़ है। इस प्रकार, कृपया यह न कहें कि यह बेकार है: D
प्रोग्रामर

7
@Programmer: stackoverflow.com/questions/12044357/… देखें । सारांश: सबसे अधिक बार जो coutधीमा है, यह इसलिए है क्योंकि आपने उपयोग किया है std::endlजहां आपको नहीं करना चाहिए।
जेरी कॉफिन

29
विशिष्ट C ++ विशेषज्ञ अहंकार। यदि प्रिंटफ मौजूद है, तो इसका उपयोग क्यों नहीं करते?
कुरो नी ने

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



1

मुख्य कारण शायद यह है कि सी ++ स्ट्रिंग एक संरचना है जिसमें एक वर्तमान-लंबाई मान शामिल है, न कि 0 बाइट द्वारा समाप्त किए गए वर्णों के अनुक्रम का पता। Printf और उसके रिश्तेदारों को इस तरह के अनुक्रम को खोजने की उम्मीद है, न कि एक संरचना, और इसलिए सी ++ स्ट्रिंग्स द्वारा भ्रमित हो।

खुद के लिए बोलते हुए, मेरा मानना ​​है कि प्रिंटफ़ में एक जगह है जिसे आसानी से C ++ सिनेटिक फीचर्स द्वारा भरा नहीं जा सकता है, जैसे कि html में टेबल संरचनाओं में एक जगह होती है जिसे आसानी से divs द्वारा नहीं भरा जा सकता। जैसा कि डायकस्ट्रा ने गोटो के बारे में बाद में लिखा था, वह एक धर्म शुरू करने का इरादा नहीं रखता था और वास्तव में केवल खराब डिजाइन वाले कोड के लिए इसे एक कीचड़ के रूप में उपयोग करने के खिलाफ बहस कर रहा था।

यह काफी अच्छा होगा यदि GNU प्रोजेक्ट अपने g ++ एक्सटेंशन में प्रिंटफ परिवार को जोड़ देगा।


1

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

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


1

printfतर्कों की एक चर संख्या को स्वीकार करता है। वे केवल सादे पुराने डेटा (POD) प्रकार के हो सकते हैं। कोड जो POD के अलावा किसी और चीज को printfकेवल संकलित करता है क्योंकि संकलक मानता है कि आपको अपना प्रारूप सही मिला है। %sइसका अर्थ है कि संबंधित तर्क को सूचक माना जाता है char। आपके मामले में यह एक std::stringनहीं है const char*printfइसे नहीं जानता क्योंकि तर्क प्रकार खो जाता है और प्रारूप पैरामीटर से पुनर्स्थापित किया जाना चाहिए। परिणामी सूचक std::stringमें उस तर्क को मोड़ने पर const char*, आपके इच्छित सी स्ट्रिंग के बजाय स्मृति के कुछ अप्रासंगिक क्षेत्र को इंगित किया जाएगा। उस कारण से आपका कोड जिबरिश छापता है।

जबकि printfहै स्वरूपित पाठ को प्रिंट लिए एक उत्कृष्ट पसंद , (खासकर यदि आप पैडिंग की इरादा), यह खतरनाक अगर आप संकलक चेतावनी को सक्षम नहीं किया जा सकता है। हमेशा चेतावनी को सक्षम करें क्योंकि तब इस तरह की गलतियों को आसानी से टाल दिया जाता है। अनाड़ी std::coutतंत्र का उपयोग करने का कोई कारण नहीं है यदि printfपरिवार एक ही कार्य को बहुत तेज और सावधानी से कर सकता है। बस सुनिश्चित करें कि आपने सभी चेतावनियाँ सक्षम कर दी हैं ( -Wall -Wextra) और आप अच्छे होंगे। यदि आप अपने स्वयं के कस्टम printfकार्यान्वयन का उपयोग करते हैं, तो आपको इसे उस __attribute__तंत्र के साथ घोषित करना चाहिए जो कंपाइलर को प्रदान किए गए मापदंडों के खिलाफ प्रारूप स्ट्रिंग की जांच करने में सक्षम बनाता है

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