एक एकल तर्क (रूपांतरण विनिर्देशक के बिना) के साथ क्यों प्रिंट किया जाता है?


102

एक पुस्तक में, जो मैं पढ़ रहा हूं, यह लिखा है कि printfएक एकल तर्क के साथ (रूपांतरण विनिर्देशक के बिना) पदावनत किया जाता है। यह स्थानापन्न करने की सिफारिश करता है

printf("Hello World!");

साथ में

puts("Hello World!");

या

printf("%s", "Hello World!");

क्या कोई मुझे बता सकता है कि printf("Hello World!");गलत क्यों है? पुस्तक में लिखा गया है कि इसमें कमजोरियां हैं। ये कमजोरियां क्या हैं?


34
नोट: printf("Hello World!")जैसा है वैसा नहीं है puts("Hello World!")puts()append a '\n'। इसके बजाय तुलना printf("abc")करेंfputs("abc", stdout)
chux -

5
वह किताब क्या है? मुझे नहीं लगता printfकि उसी तरह getsसे पदावनत किया जाता है, उदाहरण के लिए C99 में पदावनत किया जाता है, इसलिए आप अपने प्रश्न को अधिक सटीक बनाने के लिए संपादन पर विचार कर सकते हैं।
el.pescado

14
ऐसा लगता है कि जिस पुस्तक को आप पढ़ रहे हैं वह बहुत अच्छी नहीं है - एक अच्छी पुस्तक को केवल कुछ ऐसा नहीं कहना चाहिए जो "पदावनत" हो (यह तथ्यात्मक रूप से गलत है जब तक कि लेखक स्वयं की राय का वर्णन करने के लिए शब्द का उपयोग नहीं कर रहा है) और यह बताए कि क्या उपयोग है वास्तव में अवैध और खतरनाक है बजाय सुरक्षित / वैध कोड के कुछ दिखाने के उदाहरण के रूप में जो आपको "नहीं करना चाहिए"।
आर .. गिटहब स्टॉप हेल्पिंग ICE

8
क्या आप पुस्तक की पहचान कर सकते हैं?
कीथ थॉम्पसन

7
कृपया पुस्तक का शीर्षक, लेखक और पृष्ठ संदर्भ निर्दिष्ट करें। धन्यवाद।
ग्रीननलाइन

जवाबों:


122

printf("Hello World!"); क्या IMHO कमजोर नहीं है, लेकिन इस पर विचार करें:

const char *str;
...
printf(str);

यदि strकिसी स्ट्रिंग को इंगित करने के लिए %sप्रारूप निर्दिष्टकर्ता होता है, तो आपका कार्यक्रम अपरिभाषित व्यवहार (ज्यादातर दुर्घटना) का puts(str)प्रदर्शन करेगा , जबकि जैसा है वैसा ही स्ट्रिंग प्रदर्शित करेगा।

उदाहरण:

printf("%s");   //undefined behaviour (mostly crash)
puts("%s");     // displays "%s\n"

21
आगे कार्यक्रम को दुर्घटनाग्रस्त करने के लिए, प्रारूप के तार के साथ कई अन्य कारनामे संभव हैं। अधिक जानकारी के लिए यहां देखें: en.wikipedia.org/wiki/Uncontrolled_format_string
e.dan

9
एक और कारण यह है कि putsसंभवतः तेजी से होगा।
edmz

38
@black: puts"संभवतः" तेजी से है, और यह शायद एक और कारण है कि लोग इसे सुझाते हैं, लेकिन यह वास्तव में तेज़ नहीं है । मैंने सिर्फ "Hello, world!"1,000,000 बार, दोनों तरीकों से छापा । इसके साथ printf0.92 सेकंड का समय लगा। इसके साथ puts0.93 सेकंड का समय लगा। जब दक्षता की बात आती है तो चिंता की बात होती है, लेकिन printfबनाम putsउनमें से एक नहीं है।
स्टीव समिट

10
@KonstantinWeitz: लेकिन (ए) मैं जीसीसी का उपयोग नहीं कर रहा था, और (बी) इससे कोई फर्क नहीं पड़ता कि दावा " putsतेज" गलत है, यह अभी भी गलत है।
स्टीव समिट

6
@KonstantinWeitz: जिस दावे के लिए मैंने प्रमाण प्रस्तुत किया था, वह (विपरीत) उपयोगकर्ता का दावा काला कर रहा था। मैं केवल यह स्पष्ट करने की कोशिश कर रहा हूं कि प्रोग्रामर को putsइस कारण से कॉल करने के बारे में चिंतित नहीं होना चाहिए । (लेकिन अगर आप इसके बारे में बहस करना चाहते थे: मुझे आश्चर्य होगा कि क्या आप किसी भी आधुनिक मशीन के लिए कोई आधुनिक कंपाइलर पा सकते हैं, जो किसी भी परिस्थिति putsमें काफी तेज printfहै।)
स्टीव समिट

75

printf("Hello world");

ठीक है और कोई सुरक्षा भेद्यता नहीं है।

समस्या यह है कि:

printf(p);

जहां pएक इनपुट के लिए एक संकेतक है जो उपयोगकर्ता द्वारा नियंत्रित किया जाता है। यह स्ट्रिंग्स हमलों को प्रारूपित करने के लिए प्रवण है : उपयोगकर्ता प्रोग्राम के नियंत्रण के लिए रूपांतरण विनिर्देशों को सम्मिलित कर सकता है, उदाहरण के लिए, %xमेमोरी को डंप %nकरने या मेमोरी को अधिलेखित करने के लिए।

ध्यान दें कि puts("Hello world")व्यवहार के बराबर नहीं है, printf("Hello world")लेकिन करने के लिए printf("Hello world\n")। कंपाइलर आमतौर पर बाद की कॉल को इसके साथ बदलने के लिए अनुकूलित करने के लिए पर्याप्त स्मार्ट होते हैं puts


10
printf(p,x)यदि उपयोगकर्ता का नियंत्रण है तो निश्चित ही यह समस्याग्रस्त होगा p। तो समस्या सिर्फ एक तर्क के साथ नहीं है , printfबल्कि एक उपयोगकर्ता-नियंत्रित प्रारूप स्ट्रिंग के साथ है।
हेगन वॉन एतेज़ेन

2
@HagenvonEitzen यह तकनीकी रूप से सच है, लेकिन कुछ लोग जानबूझकर एक उपयोगकर्ता-प्रदान प्रारूप स्ट्रिंग का उपयोग करेंगे। जब लोग लिखते हैं printf(p), यह इसलिए है क्योंकि उन्हें एहसास नहीं है कि यह एक प्रारूप स्ट्रिंग है, वे बस सोचते हैं कि वे एक शाब्दिक मुद्रण कर रहे हैं।
बरमार

33

अन्य उत्तरों के आगे, printf("Hello world! I am 50% happy today")बनाने के लिए एक आसान बग है, संभावित रूप से सभी प्रकार की खराब मेमोरी समस्याओं (यह यूबी है!)।

यह सिर्फ सरल, आसान और अधिक मजबूत है "प्रोग्रामर" की आवश्यकता होती है जब वे पूरी तरह से स्पष्ट हो जाते हैं जब वे एक क्रियात्मक स्ट्रिंग चाहते हैं और कुछ नहीं

और यही printf("%s", "Hello world! I am 50% happy today")आपको मिलता है। यह पूरी तरह से फुलप्रूफ है।

(स्टीव, निश्चित रूप printf("He has %d cherries\n", ncherries)से एक ही बात नहीं है; इस मामले में, प्रोग्रामर "वर्बेटिम" "मानसिकता" में नहीं है; वह "प्रारूप स्ट्रिंग" मानसिकता में है।)


2
यह एक तर्क के लायक नहीं है, और मैं समझता हूं कि आप वर्बेटिम बनाम प्रारूप स्ट्रिंग मानसिकता के बारे में क्या कह रहे हैं, लेकिन, ठीक है, हर कोई इस तरह से नहीं सोचता है, जो एक कारण है एक आकार-फिट-सभी नियम रैंक कर सकते हैं। यह कहना कि "कभी भी निरंतर स्ट्रिंग्स के साथ प्रिंट न करें printf" बिल्कुल वैसा ही है जैसे कि "हमेशा लिखें" if(NULL == p)। ये नियम कुछ प्रोग्रामर्स के लिए उपयोगी हो सकते हैं, लेकिन सभी के लिए नहीं। और दोनों ही मामलों में (बेमेल printfफॉर्मेट और योदा कंडीशन), आधुनिक कंपाइलर वैसे भी गलतियों के लिए चेतावनी देते हैं। इसलिए कृत्रिम नियम और भी कम महत्वपूर्ण हैं।
स्टीव समिट

1
@Steve यदि किसी वस्तु का उपयोग करने के लिए बिल्कुल शून्य अपसाइड हैं, लेकिन काफी कुछ डाउनसाइड हैं, तो हाँ वास्तव में इसका उपयोग करने का कोई कारण नहीं है। दूसरी ओर योदा की स्थिति करते हैं कि वे कोड कठिन बना (आप सहज कहेंगे नहीं "अगर पी शून्य है" "यदि शून्य पी है") को पढ़ने के लिए नकारात्मक पहलू है।
वू

2
@Voo printf("%s", "hello")की तुलना में धीमा होने जा रहा है printf("hello"), इसलिए एक नकारात्मक पहलू है। एक छोटा सा, क्योंकि आईओ लगभग हमेशा ऐसे सरल स्वरूपण की तुलना में धीमा होता है, लेकिन एक नकारात्मक पहलू।
यक - एडम नेवेरुमोंट

1
@ यक मुझे शक है कि धीमी होगी
एमएम

gcc -Wall -W -Werrorऐसी गलतियों से बुरे परिणामों को रोकेंगे।
चकरली

17

मैं यहाँ भेद्यता भाग के बारे में थोड़ी जानकारी जोड़ूँगा

यह कहा जाता है कि प्रिंटफ स्ट्रिंग प्रारूप भेद्यता के कारण यह असुरक्षित है। आपके उदाहरण में, जहां स्ट्रिंग हार्डकोड है, यह हानिरहित है (भले ही हार्डकोडिंग तार इस तरह पूरी तरह से अनुशंसित नहीं है)। लेकिन पैरामीटर्स के प्रकारों को निर्दिष्ट करना एक अच्छी आदत है। इस उदाहरण को लें:

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

यह (और अभी भी है) बहुत छुपी हुई जानकारी या उदाहरण के लिए प्रमाणीकरण को दरकिनार करने के लिए ढेर तलाशने में कार्यक्रमों का फायदा उठाने के लिए इस्तेमाल किया गया था।

उदाहरण (C):

int main(int argc, char *argv[])
{
    printf(argv[argc - 1]); // takes the first argument if it exists
}

अगर मैं इस कार्यक्रम के इनपुट के रूप में रखूं "%08x %08x %08x %08x %08x\n"

printf ("%08x %08x %08x %08x %08x\n"); 

यह प्रिंटफ-फ़ंक्शन को स्टैक से पांच मापदंडों को प्राप्त करने और उन्हें 8-अंकीय गद्देदार हेक्साडेसिमल संख्याओं के रूप में प्रदर्शित करने का निर्देश देता है। तो एक संभावित उत्पादन की तरह लग सकता है:

40012980 080628c4 bffff7a4 00000005 08059c04

अधिक संपूर्ण स्पष्टीकरण और अन्य उदाहरणों के लिए इसे देखें ।


13

printfशाब्दिक प्रारूप स्ट्रिंग्स के साथ कॉल करना सुरक्षित और कुशल है, और यदि printfउपयोगकर्ता द्वारा उपलब्ध कराए गए प्रारूप स्ट्रिंग्स असुरक्षित हैं, तो आपके द्वारा आह्वान करने के लिए स्वचालित रूप से चेतावनी देने के लिए मौजूद उपकरण मौजूद हैं ।

प्रारूप विनिर्देश printfका लाभ लेने पर सबसे गंभीर हमले %n। अन्य सभी प्रारूप निर्दिष्टकर्ताओं के विपरीत, उदाहरण के लिए %d, %nवास्तव में एक प्रारूप तर्क में दिए गए स्मृति पते के लिए एक मूल्य लिखता है। इसका मतलब यह है कि एक हमलावर स्मृति को अधिलेखित कर सकता है और इस प्रकार संभावित रूप से आपके कार्यक्रम को नियंत्रित कर सकता है। विकिपीडिया अधिक विस्तार प्रदान करता है।

यदि आप printfशाब्दिक प्रारूप स्ट्रिंग के साथ कॉल करते हैं, तो एक हमलावर %nआपके प्रारूप स्ट्रिंग में घुस नहीं सकता है , और आप इस प्रकार सुरक्षित हैं। वास्तव में, जीसीसी अपने कॉल करने के लिए बदल जाएगा printfके लिए एक कॉल में putsतो वहाँ litteraly, कोई फर्क (परीक्षण इस चलाकर नहीं है gcc -O3 -S)।

यदि आप printfकिसी उपयोगकर्ता द्वारा प्रदान किए गए प्रारूप स्ट्रिंग के साथ कॉल करते हैं, तो एक हमलावर संभावित रूप %nसे आपके प्रारूप स्ट्रिंग में घुस सकता है , और आपके प्रोग्राम का नियंत्रण ले सकता है। आपका कंपाइलर आमतौर पर आपको चेतावनी देगा कि उसका असुरक्षित है, देखें -Wformat-security। अधिक उन्नत उपकरण भी हैं जो यह सुनिश्चित करते हैं कि printfउपयोगकर्ता द्वारा प्रदान किए गए प्रारूप स्ट्रिंग के साथ भी एक आह्वान सुरक्षित है, और वे यह भी जांच सकते हैं कि आप सही संख्या और प्रकार के तर्क पास करते हैं printf। उदाहरण के लिए, जावा के लिए Google की त्रुटि प्रवण और चेकर फ्रेमवर्क है


12

यह गुमराह करने वाली सलाह है। हां, यदि आपके पास प्रिंट करने के लिए रन-टाइम स्ट्रिंग है,

printf(str);

काफी खतरनाक है, और आपको हमेशा उपयोग करना चाहिए

printf("%s", str);

इसके बजाय, क्योंकि सामान्य तौर पर आप कभी नहीं जान सकते हैं कि क्या strकोई %संकेत हो सकता है । हालांकि, यदि आपके पास एक संकलन-समय निरंतर स्ट्रिंग है, तो इसके साथ कुछ भी गलत नहीं है

printf("Hello, world!\n");

(अन्य बातों के अलावा, यह अब तक का सबसे क्लासिक सी प्रोग्राम है, जिसका शाब्दिक अर्थ है उत्पत्ति की सी प्रोग्रामिंग बुक। इसलिए किसी को भी इस बात का ध्यान रखना कि इसका उपयोग विधिपूर्वक किया जा रहा है, और मैं एक के लिए कुछ हद तक आहत हो जाऊंगा!)


because printf's first argument is always a constant stringमैं इस बारे में बिल्कुल निश्चित नहीं हूं कि आपका क्या मतलब है।
सेबस्टियन मच

जैसा कि मैंने कहा, "He has %d cherries\n"एक निरंतर स्ट्रिंग है, जिसका अर्थ है कि यह एक संकलन-समय स्थिर है। लेकिन, निष्पक्ष होने के लिए, लेखक की सलाह "निरंतर printfतर्क के रूप में निरंतर स्ट्रिंग्स पास न करें " थी, यह "स्ट्रिंग्स को पहले तर्क के बिना पारित नहीं %करता printfहै।"
स्टीव समिट

literally from the C programming book of Genesis. Anyone deprecating that usage is being quite offensively heretical- आपने हाल के वर्षों में वास्तव में K & R नहीं पढ़ा है। वहाँ सलाह और कोडिंग शैलियों का एक टन है जो इन दिनों न केवल पदावनत किया गया है, बल्कि केवल सादे बुरे अभ्यास भी है।
वू

@Voo: ठीक है, चलो यह कहते हैं कि बुरा व्यवहार माना जाने वाला सब कुछ वास्तव में बुरा अभ्यास नहीं है। ( intदिमाग को "कभी सादे " स्प्रिंग्स का उपयोग करने की सलाह नहीं ।)
स्टीव समिट

1
@ मुझे पता नहीं है कि आपने कहाँ सुना है, लेकिन यह निश्चित रूप से उस तरह का बुरा (बुरा?) अभ्यास नहीं है जिसके बारे में हम बात कर रहे हैं। मुझे गलत न समझें, उस समय के लिए कोड पूरी तरह से ठीक था, लेकिन आप वास्तव में इन दिनों के लिए बहुत ज्यादा नहीं बल्कि एक ऐतिहासिक नोट के रूप में देखना चाहते हैं। "यह k & r में है" बस इन दिनों अच्छी गुणवत्ता का संकेतक नहीं है, यह सब है
Voo

9

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


8

चूंकि किसी ने उल्लेख नहीं किया है, मैं उनके प्रदर्शन के संबंध में एक नोट जोड़ूंगा।

सामान्य परिस्थितियों में, कोई संकलक अनुकूलन का उपयोग नहीं किया जाता है (अर्थात printf()वास्तव में कॉल printf()और नहीं fputs()), मैं printf()कम कुशलतापूर्वक प्रदर्शन करने की उम्मीद करूंगा , विशेष रूप से लंबे समय तक तार के लिए। ऐसा इसलिए है क्योंकि printf()किसी भी रूपांतरण विनिर्देशक हैं, यह जांचने के लिए स्ट्रिंग को पार्स करना है।

इसकी पुष्टि के लिए, मैंने कुछ परीक्षण चलाए हैं। परीक्षण 14.44 के साथ Ubuntu 14.04 पर किया जाता है। मेरी मशीन Intel i5 cpu का उपयोग करती है। परीक्षण किया जा रहा कार्यक्रम इस प्रकार है:

#include <stdio.h>
int main() {
    int count = 10000000;
    while(count--) {
        // either
        printf("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM");
        // or
        fputs("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM", stdout);
    }
    fflush(stdout);
    return 0;
}

दोनों का संकलन है gcc -Wall -O0। समय का उपयोग करके मापा जाता है time ./a.out > /dev/null। निम्नलिखित एक विशिष्ट रन का परिणाम है (मैंने उन्हें पांच बार चलाया है, सभी परिणाम 0.002 सेकंड के भीतर हैं)।

के लिए printf()संस्करण:

real    0m0.416s
user    0m0.384s
sys     0m0.033s

के लिए fputs()संस्करण:

real    0m0.297s
user    0m0.265s
sys     0m0.032s

यदि आपके पास बहुत लंबी स्ट्रिंग है तो यह प्रभाव बढ़ जाता है ।

#include <stdio.h>
#define STR "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
#define STR2 STR STR
#define STR4 STR2 STR2
#define STR8 STR4 STR4
#define STR16 STR8 STR8
#define STR32 STR16 STR16
#define STR64 STR32 STR32
#define STR128 STR64 STR64
#define STR256 STR128 STR128
#define STR512 STR256 STR256
#define STR1024 STR512 STR512
int main() {
    int count = 10000000;
    while(count--) {
        // either
        printf(STR1024);
        // or
        fputs(STR1024, stdout);
    }
    fflush(stdout);
    return 0;
}

के लिए printf()संस्करण (तीन बार, असली प्लस / शून्य से 1.5s भाग गया):

real    0m39.259s
user    0m34.445s
sys     0m4.839s

के लिए fputs()संस्करण (तीन बार, असली प्लस / शून्य से 0.2s भाग गया):

real    0m12.726s
user    0m8.152s
sys     0m4.581s

नोट: gcc द्वारा निर्मित असेंबली का निरीक्षण करने के बाद, मैंने महसूस किया कि gcc fputs()कॉल को fwrite()कॉल के साथ अनुकूलित करता है , यहाँ तक कि साथ भी -O0। ( printf()कॉल अपरिवर्तित रहता है।) मुझे यकीन नहीं है कि क्या यह मेरे परीक्षण को अमान्य कर देगा, क्योंकि संकलक fwrite()संकलन-समय पर स्ट्रिंग की लंबाई की गणना करता है।


2
यह आपके परीक्षण को अमान्य नहीं करेगा, जैसा fputs()कि अक्सर स्ट्रिंग स्थिरांक के साथ प्रयोग किया जाता है और यह अनुकूलन अवसर उस बिंदु का हिस्सा होता है जिसे आप बनाना चाहते थे। यह कहा, एक गतिशील रूप से उत्पन्न स्ट्रिंग के साथ एक परीक्षण रन जोड़ना fputs()और fprintf()एक अच्छा पूरक डेटा बिंदु होगा। ।
पैट्रिक श्ल्टर

@ पैट्रिक श्लटर परीक्षण गतिशील रूप से उत्पन्न स्ट्रिंग्स के साथ इस प्रश्न के उद्देश्य को पराजित करने के लिए लगता है, हालांकि ... ओपी केवल स्ट्रिंग शाब्दिकों में मुद्रित होने के लिए रुचि रखता है।
user12205

1
वह स्पष्ट रूप से यह नहीं बताता है, भले ही उसका उदाहरण स्ट्रिंग शाब्दिक का उपयोग करता हो। वास्तव में, मुझे लगता है कि पुस्तक की सलाह के बारे में उनका भ्रम उदाहरण में स्ट्रिंग शाब्दिक के उपयोग का परिणाम है। स्ट्रिंग शाब्दिक के साथ, किताबों की सलाह किसी भी तरह संदिग्ध है, गतिशील तार के साथ यह अच्छी सलाह है।
पैट्रिक शाल्टर

1
/dev/nullइस तरह से यह एक खिलौना बनाता है, जिसमें आम तौर पर स्वरूपित आउटपुट उत्पन्न करते समय, आपका लक्ष्य आउटपुट के लिए कहीं जाना है, त्याग नहीं किया जाना चाहिए। एक बार जब आप "वास्तव में डेटा को त्याग नहीं" समय में जोड़ते हैं, तो वे कैसे तुलना करते हैं?
यक्क - एडम नेवरामोंट

7
printf("Hello World\n")

स्वचालित रूप से समकक्ष के लिए संकलित करता है

puts("Hello World")

आप इसे अपने निष्पादन योग्य को हटाने के साथ जांच सकते हैं:

push rbp
mov rbp,rsp
mov edi,str.Helloworld!
call dword imp.puts
mov eax,0x0
pop rbp
ret

का उपयोग करते हुए

char *variable;
... 
printf(variable)

सुरक्षा के मुद्दों के लिए नेतृत्व करेंगे, कभी भी इस तरह से प्रिंट का उपयोग न करें!

इसलिए आपकी पुस्तक वास्तव में सही है, एक चर के साथ प्रिंटफ का उपयोग करना पदावनत है, लेकिन आप अभी भी प्रिंटफ ("मेरा स्ट्रिंग \ n") का उपयोग कर सकते हैं क्योंकि यह स्वचालित रूप से पुट बन जाएगा।


12
यह व्यवहार वास्तव में पूरी तरह से कंपाइलर पर निर्भर करता है।
जाबरवॉकी

6
यह भ्रामक है। आप राज्य करते हैं A compiles to B, लेकिन वास्तव में आपका मतलब है A and B compile to C
सेबेस्टियन मच

6

जीसीसी के लिए यह संभव है कि चेकिंग के लिए विशिष्ट चेतावनियां printf()और scanf()

जीसीसी प्रलेखन राज्यों:

-Wformatमें शामिल है -Wall। जाँच, विकल्प प्रारूप के कुछ पहलुओं पर अधिक नियंत्रण के लिए -Wformat-y2k, -Wno-format-extra-args, -Wno-format-zero-length, -Wformat-nonliteral, -Wformat-security, और -Wformat=2में शामिल नहीं हैं उपलब्ध हैं, लेकिन -Wall

-Wformatजिसके भीतर सक्षम किया गया है -Wallविकल्प कई विशेष चेतावनी है कि मदद के इन मामलों को खोजने के लिए सक्षम नहीं करता है:

  • -Wformat-nonliteral यदि आप स्ट्रिंग स्पेसल को प्रारूप विनिर्देशक के रूप में पारित नहीं करते हैं तो चेतावनी देंगे।
  • -Wformat-securityयदि आप एक स्ट्रिंग है जो एक खतरनाक निर्माण शामिल हो सकता है चेतावनी देते हैं। इसका एक सबसेट है -Wformat-nonliteral

मुझे यह स्वीकार करना होगा कि -Wformat-securityहमारे कोडबेस (लॉगिंग मॉड्यूल, एरर हैंडलिंग मॉड्यूल, xml आउटपुट मॉड्यूल, में हमारे पास मौजूद कई बग्स को सक्षम करने के लिए , सभी में कुछ ऐसे कार्य थे जो अपरिभाषित चीजों को कर सकते थे यदि उन्हें उनके पैरामीटर में% वर्णों के साथ बुलाया गया हो। जानकारी के लिए। हमारा कोडबेस अब लगभग 20 साल का है और भले ही हमें इस तरह की समस्याओं के बारे में पता हो, लेकिन जब हमने इन चेतावनियों को कोडबेस में कितने बग थे तब भी हमें सक्षम करते हुए बेहद आश्चर्यचकित थे।


1

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


printfएक एकल तर्क (रूपांतरण विनिर्देशक के बिना) के साथ पदावनत क्यों किया जाता है ?

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

C पूरी दुनिया के बीच, स्टेटस शुरुआतकर्ता से लेकर स्टेटस एक्सपर्ट printfकंसोल तक आउटपुट के रूप में एक साधारण टेक्स्ट वाक्यांश देने के लिए उस तरह का उपयोग करते हैं।

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

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

उस स्ट्रिंग के "Hello World!"अंदर किसी भी प्रारूप विनिर्देशक के बिना केवल तर्क के रूप में एक सरल स्ट्रिंग शाब्दिक देते हुए कहा, जैसे कि आपने इसे प्रश्न में प्रदान किया है:

printf("Hello World!");

है पदावनत या " बुरा व्यवहार " बिल्कुल न ही किसी भी कमजोरियों है।

वास्तव में, कई सी प्रोग्रामर शुरू करते हैं और सामान्य रूप से उस हैलोवर्ल्ड-प्रोग्राम के साथ सी या यहां तक ​​कि प्रोग्रामिंग भाषाओं को सीखना और उपयोग करना शुरू करते हैं और यह printfकथन अपनी तरह के पहले वाले हैं।

वे नहीं होगा कि अगर वे पदावनत हो गए।

एक पुस्तक में, जो मैं पढ़ रहा हूं, यह लिखा है कि printfएक एकल तर्क के साथ (रूपांतरण विनिर्देशक के बिना) पदावनत किया जाता है।

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

जो मैंने ऊपर कहा, उसके अनुसार printfकेवल एक तर्क (एक स्ट्रिंग शाब्दिक) और बिना किसी प्रारूप के विनिर्देशक का उपयोग करना किसी भी मामले में पदावनत या "बुरा व्यवहार" नहीं माना जाता है ।

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


आप जोड़ सकते हैं कि printf("Hello World!");है नहीं के बराबर puts("Hello World!");वैसे भी, जो सिफारिश के लेखक के बारे में कुछ बताता है।
चकरली
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.