दिलचस्प पोस्ट
PHP का उपयोग करने के लिए एक फ़ाइल की सेवा के लिए तेज़ तरीका हैशिंग फ़ंक्शन क्या हैशेट क्लास को लागू करने के लिए जावा का उपयोग करता है? अपवाद नहीं फेंक दिए जाने पर ब्लॉक / प्रदर्शन को रोकने के प्रयास करें? मैं कैसे जांच करूं अगर स्ट्रिंग में स्विफ्ट में कोई अन्य स्ट्रिंग है? Scanf () समस्या से पहले C / C ++ printf () जर्सी अनुरोध के संदर्भ में एक वस्तु इंजेक्षन कैसे करें? क्यूटी क्रिएटर प्रोजेक्ट में बाह्य पुस्तकालय को जोड़ना मैं सी # में एक फ़ाइल को स्ट्रीम कैसे सहेजूं? किंवदंती के साथ Matplotlib तितर बितर प्लॉट एक दांतेदार सरणी क्या है? उचित codeigniter आधार url कैसे सेट करें? लोडर प्रदर्शित करते समय उल्का संग्रह भार iPhone नेविगेशन बार शीर्षक टेक्स्ट का रंग डिफ़ॉल्ट-पैकेज में जावा-कक्षाओं को कैसे एक्सेस करना है? GluSphere () का उपयोग किए बिना ओपनजीएल में क्षेत्र को आकर्षित करना?

सी – स्कैनफ़ () बनाम हो जाता है () vs fgets ()

मैं पूर्णांक को वर्णों की एक स्ट्रिंग (गिनती संख्याओं को दर्ज किया जाता है) में कनवर्ट करने का एक काफी आसान प्रोग्राम कर रहा हूं

मेरे द्वारा किए जाने के बाद, मैंने कुछ बहुत ही अजीब "बग" देखे, जो मैं जवाब नहीं दे सकता, ज्यादातर के कारण मेरे सीमित ज्ञान के कारण scanf() , gets() और fgets() फ़ंक्शन काम करता है (मैंने हालांकि बहुत सारे साहित्य पढ़ा।)

तो बहुत अधिक टेक्स्ट लिखे बिना, यहां कार्यक्रम का कोड है:

 #include <stdio.h> #define MAX 100 int CharToInt(const char *); int main() { char str[MAX]; printf(" Enter some numbers (no spaces): "); gets(str); // fgets(str, sizeof(str), stdin); // scanf("%s", str); printf(" Entered number is: %d\n", CharToInt(str)); return 0; } int CharToInt(const char *s) { int i, result, temp; result = 0; i = 0; while(*(s+i) != '\0') { temp = *(s+i) & 15; result = (temp + result) * 10; i++; } return result / 10; } 

तो यहाँ की समस्या मैं कर रहा हूं। सबसे पहले, जब gets() फ़ंक्शन का उपयोग करते हुए, प्रोग्राम पूरी तरह से काम करता है

दूसरा, fgets() का fgets() करते समय, परिणाम थोड़ा गलत है क्योंकि जाहिरा तौर पर fgets() फ़ंक्शन न्यूलाइन (एएससीआईआई मान 10) चरित्र पढ़ता है जो परिणाम के ऊपर स्क्रू करता है

तीसरा, जब scanf() फ़ंक्शन का उपयोग करते हुए, परिणाम पूरी तरह से गलत है क्योंकि पहले वर्ण में जाहिरा तौर पर एक -52 एएससीआईआई मान है। इसके लिए, मेरे पास कोई स्पष्टीकरण नहीं है।

अब मुझे पता है कि gets() को उपयोग करने के लिए निराश किया gets() है, इसलिए मैं जानना चाहूंगा कि मैं यहाँ fgets() का उपयोग कर सकता हूं, ताकि वह नएलाइन वाले वर्ण को पढ़ा न जाए। इसके अलावा, इस कार्यक्रम में scanf() फ़ंक्शन के साथ क्या सौदा है?

वेब के समाधान से एकत्रित समाधान "सी – स्कैनफ़ () बनाम हो जाता है () vs fgets ()"

  • कभी भी उपयोग नहीं होता यह बफर ओवरफ्लो भेद्यता के खिलाफ कोई सुरक्षा प्रदान नहीं करता है (यानी, आप यह नहीं बता सकते कि बफर आप कितना बड़ा करते हैं, इसलिए यह किसी उपयोगकर्ता को बफर से बड़ा एक पंक्ति दर्ज करने से रोक नहीं सकता और स्मृति को रोकना)।

  • scanf का उपयोग करने से बचें अगर सावधानी से उपयोग नहीं किया जाता है, तो उसे बफर अतिप्रवाह समस्याएं gets सकती हैं जैसे कि gets यहां तक ​​कि अनदेखी करते हुए, इसमें अन्य समस्याएं हैं जो सही तरीके से उपयोग करने में कठोर हैं ।

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

हाँ, आप से बचने के लिए चाहते हैं fgets हमेशा नई लाइन पढ़ा होगा यदि बफर इसे पकड़ने के लिए पर्याप्त बड़ा था (जो आपको बफ़र बहुत छोटा था और पढ़ने के लिए प्रतीक्षा करने वाली रेखा से अधिक है)। यदि आप कुछ ऐसे fgets चाहते हैं जो नई लाइन को नहीं fgets हैं (बहुत छोटा बफर के उस संकेत को खोने से) आप एक स्कैन-सेट रूपांतरण के साथ fscanf उपयोग कर सकते हैं जैसे "%N[^\n]" , जहां 'एन' को बफर आकार से बदल दिया गया है – 1

एक आसान (अगर अजीब) fgets साथ पढ़ने के बाद एक बफर से पिछली नई लाइन को निकालने का तरीका है: strtok(buffer, "\n"); यह ऐसा नहीं है कि strtok का उपयोग करने के लिए क्या करना है, लेकिन मैंने इसे वांछित फ़ैशन से अधिक बार इसे इस्तेमाल किया है (जिसे मैं आमतौर पर से बचने के लिए)।

इस कोड के साथ कई समस्याएं हैं हम बुरी तरह नामित वैरिएबल और फ़ंक्शन ठीक कर देंगे और समस्याओं की जांच करेंगे:

  • सबसे पहले, CharToInt() को उचित स्ट्रिंगटोइंट StringToInt() बदल दिया जाना चाहिए क्योंकि यह एक स्ट्रिंग पर एक अक्षर नहीं चलती है

  • फ़ंक्शन CharToInt() [एसआईसी।] असुरक्षित है। यह जांच नहीं करता कि उपयोगकर्ता गलती से एक शून्य पॉइंटर में गुजरता है या नहीं।

  • यह इनपुट को मान्य नहीं करता, या अधिक सही ढंग से, अमान्य इनपुट को छोड़ें। यदि उपयोगकर्ता एक गैर-अंकों में प्रवेश करता है तो परिणाम में एक फर्जी मान होगा। यानी अगर आप N में दर्ज करें *(s+i) & 15 का उत्पादन होगा 14!

  • इसके बाद, CharToInt() [में]। में CharToInt() को digit कहा जाना चाहिए क्योंकि यह वास्तव में क्या है।

  • इसके अलावा, क्लोद्ज return result / 10; बस यही है – एक छोटी गाड़ी कार्यान्वयन के आसपास काम करने के लिए एक बुरा हैक

  • इसी तरह MAX को बुरी तरह से नाम दिया गया है क्योंकि यह मानक उपयोग के साथ संघर्ष करने के लिए दिखाई दे सकता है। यानी #define MAX(X,y) ((x)>(y))?(x):(y)

  • वर्बोस *(s+i) केवल *s रूप में पठनीय नहीं है अभी तक एक और अस्थायी सूचकांक के साथ कोड को इस्तेमाल करने और अव्यवस्था करने की कोई आवश्यकता नहीं है i

हो जाता है ()

यह बुरा है क्योंकि यह इनपुट स्ट्रिंग बफर को ओवरफ्लो कर सकता है। उदाहरण के लिए, यदि बफर आकार 2 है, और आप 16 वर्णों में दर्ज करते हैं, तो आप str

scanf ()

यह उतना ही बुरा है क्योंकि यह इनपुट स्ट्रिंग बफर को ओवरफ्लो कर सकता है।

आप का उल्लेख " scanf () फ़ंक्शन का उपयोग करते समय, परिणाम पूरी तरह से गलत है क्योंकि पहले वर्ण में जाहिरा तौर पर एक -52 एएससीआईई मान है। "

यह scanf () के गलत उपयोग के कारण है मैं इस बग को डुप्लिकेट करने में सक्षम नहीं था

fgets ()

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

लाइन में आओ()

कुछ लोगों ने प्रतिस्थापन के रूप में सी getline() मानक getline() सुझाव दिया है। दुर्भाग्य से यह एक व्यावहारिक पोर्टेबल समाधान नहीं है क्योंकि माइक्रोसॉफ्ट सी संस्करण को लागू नहीं करता है; केवल मानक C ++ स्ट्रिंग टेम्पलेट फ़ंक्शन के रूप में यह SO # 27755191 प्रश्न उत्तर माइक्रोसॉफ्ट की सी ++ getline() कम से कम दृश्य स्टूडियो 6 के रूप में उपलब्ध था, लेकिन जब से ओपी सख्ती से सी के बारे में पूछ रहा है और सी + नहीं है यह एक विकल्प नहीं है।

विविध।

अन्त में, यह कार्यान्वयन छोटी गाड़ी है जिसमें यह पूर्णांक अतिप्रवाह का पता नहीं लगाता है। यदि उपयोगकर्ता बहुत बड़ी संख्या में प्रवेश करता है तो संख्या नकारात्मक हो सकती है! यानी 9876543210 हो जाएगा -18815698 ?! चलिए भी ठीक करें

यह एक unsigned int लिए ठीक करने के लिए तुच्छ है यदि पिछले आंशिक संख्या कम है तो वर्तमान आंशिक संख्या में हम अतिप्रवाह कर चुके हैं और हम पिछले आंशिक संख्या को वापस करते हैं।

signed int यह थोड़ा और काम है विधानसभा में हम ले-फ्लैग का निरीक्षण कर सकते हैं, लेकिन सी में हस्ताक्षर किए इंट मैथ के साथ अतिप्रवाह का पता लगाने के लिए कोई मानक निर्मित नहीं है। सौभाग्य से, जब से हम एक निरंतर, * 10 गुणा कर रहे हैं, तो हम आसानी से इसका पता लगा सकते हैं यदि हम समकक्ष समीकरण का उपयोग करते हैं:

 n = x*10 = x*8 + x*2 

यदि एक्स * 8 ओवरफ्लो तो तार्किक रूप से x * 10 भी होगा 32-बिट इंट ओवरहोल के लिए ऐसा होगा जब x * 8 = 0x100000000, हमें ऐसा करने की ज़रूरत है, जब x> = 0x20000000 चूंकि हम यह नहीं मानना ​​चाहते हैं कि कितने बिट्स को एक int है, हमें केवल यह जानने की जरूरत है कि शीर्ष 3 एमएसबी (सबसे महत्वपूर्ण बिट्स) सेट हैं।

इसके अतिरिक्त, एक दूसरे अतिप्रवाह परीक्षण की आवश्यकता है। यदि एमसीबी अंक संरेखित करने के बाद सेट (साइन बिट) है तो हमें यह भी पता है कि अधिक से अधिक संख्या में फंसे हुए हैं।

कोड

यहां कोड के साथ एक निश्चित सुरक्षित संस्करण है जो आप असुरक्षित संस्करणों में अतिप्रवाह का पता लगाने के लिए खेल सकते हैं। मैंने signed और unsigned संस्करणों को #define SIGNED 1 माध्यम से भी शामिल किया है

 #include <stdio.h> #include <ctype.h> // isdigit() // 1 fgets // 2 gets // 3 scanf #define INPUT 1 #define SIGNED 1 // re-implementation of atoi() // Test Case: 2147483647 -- valid 32-bit // Test Case: 2147483648 -- overflow 32-bit int StringToInt( const char * s ) { int result = 0, prev, msb = (sizeof(int)*8)-1, overflow; if( !s ) return result; while( *s ) { if( isdigit( *s ) ) // Alt.: if ((*s >= '0') && (*s <= '9')) { prev = result; overflow = result >> (msb-2); // test if top 3 MSBs will overflow on x*8 result *= 10; result += *s++ & 0xF;// OPTIMIZATION: *s - '0' if( (result < prev) || overflow ) // check if would overflow return prev; } else break; // you decide SKIP or BREAK on invalid digits } return result; } // Test case: 4294967295 -- valid 32-bit // Test case: 4294967296 -- overflow 32-bit unsigned int StringToUnsignedInt( const char * s ) { unsigned int result = 0, prev; if( !s ) return result; while( *s ) { if( isdigit( *s ) ) // Alt.: if (*s >= '0' && *s <= '9') { prev = result; result *= 10; result += *s++ & 0xF; // OPTIMIZATION: += (*s - '0') if( result < prev ) // check if would overflow return prev; } else break; // you decide SKIP or BREAK on invalid digits } return result; } int main() { int detect_buffer_overrun = 0; #define BUFFER_SIZE 2 // set to small size to easily test overflow char str[ BUFFER_SIZE+1 ]; // C idiom is to reserve space for the NULL terminator printf(" Enter some numbers (no spaces): "); #if INPUT == 1 fgets(str, sizeof(str), stdin); #elif INPUT == 2 gets(str); // can overflows #elif INPUT == 3 scanf("%s", str); // can also overflow #endif #if SIGNED printf(" Entered number is: %d\n", StringToInt(str)); #else printf(" Entered number is: %u\n", StringToUnsignedInt(str) ); #endif if( detect_buffer_overrun ) printf( "Input buffer overflow!\n" ); return 0; } 

आप सही हैं कि आपको कभी भी उपयोग नहीं करना चाहिए अगर आप fgets का उपयोग करना चाहते हैं, तो आप बस नई लाइन को ओवरराइट कर सकते हैं

 char *result = fgets(str, sizeof(str), stdin); char len = strlen(str); if(result != NULL && str[len - 1] == '\n') { str[len - 1] = '\0'; } else { // handle error } 

ऐसा लगता है कि कोई एम्बेडेड नल नहीं हैं। एक अन्य विकल्प POSIX getline :

 char *line = NULL; size_t len = 0; ssize_t count = getline(&line, &len, stdin); if(count >= 1 && line[count - 1] == '\n') { line[count - 1] = '\0'; } else { // Handle error } 

लाभ उठाने के लिए यह आपके लिए आवंटन और पुन: आवंटन करता है, यह संभावित एम्बेडेड नल को संभालता है, और यह गिनती देता है ताकि आपको स्ट्रेलन के साथ समय बर्बाद न करना पड़े। ध्यान दें कि आप getline साथ एक सरणी का उपयोग नहीं कर सकते संकेतक NULL या फ्री-सक्षम होना चाहिए।

मुझे यकीन नहीं है कि आप scanf साथ क्या कर रहे हैं

कभी भी उपयोग नहीं होता (), यह अप्रतिबंधित अतिप्रवाह हो सकता है यदि आपकी स्ट्रिंग सरणी 1000 के आकार का है और मैं 1001 वर्णों को दर्ज करता हूँ, तो मैं आपके प्रोग्राम को अतिप्रवाह कर सकता हूँ।

अपने CharToInt () के इस संशोधित संस्करण के साथ fgets () का उपयोग करने का प्रयास करें:

 int CharToInt(const char *s) { int i, result, temp; result = 0; i = 0; while(*(s+i) != '\0') { if (isdigit(*(s+i))) { temp = *(s+i) & 15; result = (temp + result) * 10; } i++; } return result / 10; } 

यह अनिवार्य रूप से इनपुट अंकों की पुष्टि करता है और कुछ और को अनदेखा करता है यह बहुत कच्चा है इसलिए इसे बदलने और स्वाद के लिए नमक।

इसलिए मैं ज्यादा प्रोग्रामर नहीं हूं लेकिन मुझे scanf(); के बारे में अपने प्रश्न का उत्तर देने की कोशिश करता हूं scanf(); । मुझे लगता है कि scanf बहुत ठीक है और ज्यादातर मुद्दों के बिना इसके लिए सब कुछ का उपयोग करें लेकिन आपने पूरी तरह सही संरचना नहीं ली है। यह होना चाहिए:

 char str[MAX]; printf("Enter some text: "); scanf("%s", &str); fflush(stdin); 

चर के सामने "और" महत्वपूर्ण है। यह प्रोग्राम को बताता है कि स्कैन किए गए मूल्य को बचाने के लिए (जहां चर में) है। fflush(stdin); बफर को मानक इनपुट (कीबोर्ड) से साफ़ करता है, इसलिए आपको बफर अतिप्रवाह प्राप्त होने की संभावना कम हो जाती है

और अंतर / स्कैन एफ और फास्ट्स के बीच अंतर यह gets(); और scanf(); केवल प्रथम स्थान तक स्कैन करें जबकि ' ' fgets(); पूरे इनपुट को स्कैन करता है (लेकिन बाद में बफर को साफ करना सुनिश्चित करें ताकि आप बाद में ओवरफ़्लो प्राप्त न करें)