जवाब में से कुछ यहाँ पर हस्ताक्षर किए और अहस्ताक्षरित मूल्यों के बीच आश्चर्य की बात पदोन्नति नियमों का उल्लेख है, लेकिन यह है कि अधिक से संबंधित एक समस्या की तरह लगता है मिश्रण पर हस्ताक्षर किए और अहस्ताक्षरित मूल्यों, और जरूरी स्पष्ट नहीं होता क्यों हस्ताक्षर किए चर से अधिक प्राथमिकता दी जाएगी अहस्ताक्षरित परिदृश्यों मिश्रण के बाहर।
मेरे अनुभव में, मिश्रित तुलना और पदोन्नति नियमों के बाहर, दो प्राथमिक कारण हैं कि अहस्ताक्षरित मान बग मैग्नेट निम्नानुसार हैं।
अहस्ताक्षरित मानों में शून्य पर असंतोष है, प्रोग्रामिंग में सबसे आम मूल्य है
अहस्ताक्षरित और हस्ताक्षर किए गए पूर्णांक दोनों में उनके न्यूनतम और अधिकतम मूल्यों पर एक कटाव होता है, जहां वे चारों ओर लपेटते हैं (अहस्ताक्षरित) या अपरिभाषित व्यवहार (हस्ताक्षरित) का कारण बनते हैं। के लिए unsigned
इन बिंदुओं पर कर रहे हैं शून्य और UINT_MAX
। के लिए int
वे कर रहे हैं INT_MIN
और INT_MAX
। की विशिष्ट मूल्यों INT_MIN
और INT_MAX
4 बाइट के साथ सिस्टम पर int
मान हैं -2^31
और 2^31-1
, और एक ऐसी प्रणाली पर UINT_MAX
आम तौर पर है 2^32-1
।
प्राथमिक बग-उत्प्रेरण समस्या unsigned
जो इसके साथ लागू नहीं होती है int
, वह यह है कि इसमें शून्य पर असंतोष है । शून्य, ज़ाहिर है, कार्यक्रमों में एक बहुत ही सामान्य मूल्य है, साथ ही अन्य छोटे मूल्य जैसे 1,2,3। विभिन्न निर्माणों में, विशेष रूप से 1, छोटे मानों को जोड़ना और घटाना सामान्य है, और यदि आप किसी भी चीज़ को किसी unsigned
मूल्य से घटाते हैं और यह शून्य होता है, तो आपको एक बड़े पैमाने पर सकारात्मक मूल्य और लगभग एक निश्चित बग मिला है।
पिछले 0.5 को छोड़कर सूचकांक द्वारा वेक्टर के सभी मूल्यों पर कोड पुनरावृत्तियों पर विचार करें :
for (size_t i = 0; i < v.size() - 1; i++) {
यह ठीक काम करता है जब तक कि एक दिन आप एक खाली वेक्टर में नहीं गुजरते। शून्य पुनरावृत्तियों करने के बजाय, आपको v.size() - 1 == a giant number
1 मिलता है और आप 4 बिलियन पुनरावृत्तियाँ करेंगे और लगभग एक बफर अतिप्रवाह भेद्यता होगी।
आपको इसे इस तरह लिखना होगा:
for (size_t i = 0; i + 1 < v.size(); i++) {
तो यह इस मामले में "तय" किया जा सकता है, लेकिन केवल बिना ध्यान दिए हुए प्रकृति के बारे में सोचकर size_t
। कभी-कभी आप ऊपर दिए गए फ़िक्सेस को लागू नहीं कर सकते हैं क्योंकि एक स्थिर के बजाय आपके पास कुछ वैरिएबल ऑफ़सेट हैं जिन्हें आप लागू करना चाहते हैं, जो सकारात्मक या नकारात्मक हो सकता है: इसलिए तुलना के "पक्ष" जो आपको इसे लागू करने की आवश्यकता है, हस्ताक्षर पर निर्भर करता है - अब कोड वास्तव में गड़बड़ हो जाता है ।
कोड के साथ एक समान समस्या है जो शून्य तक और इसमें शामिल करने की कोशिश करता है। कुछ while (index-- > 0)
ठीक काम करता है, लेकिन स्पष्ट रूप से समतुल्य while (--index >= 0)
एक अहस्ताक्षरित मूल्य के लिए कभी समाप्त नहीं होगा। आपका कंपाइलर आपको चेतावनी दे सकता है जब दाहिने हाथ की ओर शाब्दिक शून्य है, लेकिन निश्चित रूप से नहीं अगर यह रनटाइम पर निर्धारित मूल्य है।
मुकाबला
कुछ लोग यह तर्क दे सकते हैं कि हस्ताक्षरित मूल्यों में भी दो विसंगतियां हैं, इसलिए अहस्ताक्षरित क्यों चुनें? अंतर यह है कि दोनों असंतोष शून्य से बहुत दूर (अधिकतम) हैं। मैं वास्तव में इसे "अतिप्रवाह" की एक अलग समस्या मानता हूं, दोनों हस्ताक्षरित और अहस्ताक्षरित मूल्य बहुत बड़े मूल्यों पर बह सकते हैं। कई मामलों में मूल्यों की संभावित सीमा पर बाधाओं के कारण अतिप्रवाह असंभव है, और कई 64-बिट मानों का अतिप्रवाह शारीरिक रूप से असंभव हो सकता है)। यदि संभव हो तो, अतिप्रवाह से संबंधित बग की संभावना "शून्य पर" बग की तुलना में अक्सर माइनसक्यूल होती है , और अहस्ताक्षरित मानों के लिए भी अतिप्रवाह होता है । इसलिए अहस्ताक्षरित दोनों दुनिया के सबसे बुरे को जोड़ती है: संभावित रूप से बहुत बड़े परिमाण मूल्यों के साथ अतिप्रवाह, और शून्य पर एक असंतोष। हस्ताक्षर केवल पूर्व के पास है।
कई लोग अहस्ताक्षरित के साथ "आप थोड़ा खो देंगे" का तर्क देंगे। यह अक्सर सच होता है - लेकिन हमेशा नहीं (यदि आपको अहस्ताक्षरित मूल्यों के बीच अंतर का प्रतिनिधित्व करने की आवश्यकता है तो आप उस बिट बिट को खो देंगे: तो कई 32-बिट चीजें 2 GiB तक सीमित हैं, या आपके पास एक अजीब ग्रे क्षेत्र होगा, जहां कहेंगे एक फ़ाइल 4 GiB हो सकती है, लेकिन आप दूसरे 2 GiB आधे पर कुछ API का उपयोग नहीं कर सकते हैं)।
यहां तक कि उन मामलों में जहां अहस्ताक्षरित आपको थोड़ा खरीदता है: यह आपको ज्यादा नहीं खरीदता है: यदि आपको 2 बिलियन से अधिक "चीजों" का समर्थन करना था, तो आपको जल्द ही 4 बिलियन से अधिक का समर्थन करना होगा।
तार्किक रूप से, अहस्ताक्षरित मूल्य हस्ताक्षरित मूल्यों का सबसेट हैं
गणितीय रूप से, अहस्ताक्षरित मान (गैर-नकारात्मक पूर्णांक) हस्ताक्षरित पूर्णांक (बस _integers) कहा जाता है। २ । फिर भी हस्ताक्षर किए गए मान स्वाभाविक रूप से केवल अहस्ताक्षरित मानों, जैसे घटाव पर संचालन से बाहर निकलते हैं । हम कह सकते हैं कि अहस्ताक्षरित मान घटाव के तहत बंद नहीं हैं । हस्ताक्षरित मूल्यों का सच नहीं है।
एक फ़ाइल में दो अहस्ताक्षरित अनुक्रमित के बीच "डेल्टा" खोजना चाहते हैं? खैर आप सही क्रम में घटाव को बेहतर करते हैं, अन्यथा आपको गलत उत्तर मिलेगा। बेशक, आपको सही ऑर्डर निर्धारित करने के लिए अक्सर रनटाइम चेक की आवश्यकता होती है! जब संख्या के रूप में अहस्ताक्षरित मानों के साथ काम करते हैं, तो आप अक्सर पाएंगे कि (तार्किक रूप से) हस्ताक्षरित मूल्य वैसे भी दिखाई देते हैं, इसलिए आप हस्ताक्षर के साथ शुरू कर सकते हैं।
मुकाबला
जैसा कि ऊपर फुटनोट (2) में उल्लेख किया गया है, सी ++ में हस्ताक्षरित मूल्य वास्तव में एक ही आकार के अहस्ताक्षरित मूल्यों का एक उपसमूह नहीं हैं, इसलिए अहस्ताक्षरित मान उन परिणामों की समान संख्या का प्रतिनिधित्व कर सकते हैं जो हस्ताक्षर किए गए मान कर सकते हैं।
सच है, लेकिन सीमा कम उपयोगी है। घटाव पर विचार करें, और 0 से 2N की सीमा के साथ अहस्ताक्षरित संख्याएँ, और -N की सीमा के साथ -N पर संख्याओं के साथ हस्ताक्षर किए। मनमाना घटाव परिणाम में परिणाम -2N से 2N में _both मामलों में होता है, और या तो पूर्णांक का केवल प्रतिनिधित्व कर सकते हैं इसका आधा। वैसे यह पता चला है कि एन-एन के शून्य के आसपास केंद्रित क्षेत्र आमतौर पर 0 से 2N की सीमा की तुलना में अधिक उपयोगी होता है (वास्तविक दुनिया कोड में अधिक वास्तविक परिणाम होता है)। वर्दी (लॉग, जिप्फ़ियन, सामान्य, जो) के अलावा अन्य किसी भी सामान्य वितरण पर विचार करें और उस वितरण से बेतरतीब ढंग से चयनित मूल्यों को घटाने पर विचार करें: जिस तरह से [0, 2N] की तुलना में [-एन, एन] में अधिक मूल्य समाप्त होते हैं (वास्तव में, परिणामस्वरूप वितरण हमेशा शून्य पर केंद्रित है)।
64-बिट संख्याओं के रूप में हस्ताक्षर किए गए मानों का उपयोग करने के कई कारणों से दरवाजा बंद कर देता है
मुझे लगता है कि उपरोक्त तर्क 32-बिट मूल्यों के लिए पहले से ही मजबूर थे, लेकिन अतिप्रवाह के मामले, जो विभिन्न थ्रेसहोल्ड पर हस्ताक्षरित और अहस्ताक्षरित दोनों को प्रभावित करते हैं, 32-बिट मान के लिए होते हैं, क्योंकि "2 बिलियन" एक संख्या है जो कई से अधिक हो सकती है सार और भौतिक मात्रा (अरबों डॉलर, अरबों नैनोसेकंड, अरबों तत्वों के साथ सरणियाँ)। इसलिए अगर किसी को अहस्ताक्षरित मूल्यों के लिए सकारात्मक सीमा के दोगुने होने का यकीन है, तो वे ऐसा मामला बना सकते हैं जो अतिप्रवाह मायने रखता है और यह थोड़ा अहस्ताक्षरित है।
विशेष डोमेन के बाहर 64-बिट मान काफी हद तक इस चिंता को दूर करते हैं। हस्ताक्षरित 64-बिट मानों की ऊपरी सीमा 9,223,372,036,854,775,807 - नौ क्विंटल से अधिक है । यह बहुत अधिक नैनोसेकंड (लगभग 292 साल), और बहुत सारा पैसा है। यह किसी भी कंप्यूटर की तुलना में एक बड़ी सरणी है जिसमें लंबे समय तक एक सुसंगत पते के स्थान में रैम होने की संभावना है। तो शायद 9 क्विंटल हर किसी के लिए पर्याप्त है (अभी के लिए)?
अहस्ताक्षरित मूल्यों का उपयोग कब करें
ध्यान दें कि स्टाइल गाइड निषिद्ध संख्याओं के उपयोग को मना या आवश्यक नहीं करता है। इसके साथ समाप्त होता है:
एक अहस्ताक्षरित प्रकार का उपयोग केवल यह दावा करने के लिए न करें कि एक चर गैर-नकारात्मक है।
वास्तव में, अहस्ताक्षरित चर के लिए अच्छे उपयोग हैं:
जब आप एन-बिट मात्रा को पूर्णांक के रूप में नहीं, बल्कि बस एक "बिट्स के बैग" के रूप में व्यवहार करना चाहते हैं। उदाहरण के लिए, एक बिटमास्क या बिटमैप या एन बुलियन मान या जो भी हो। यह उपयोग अक्सर निश्चित चौड़ाई प्रकारों के साथ हाथ से हाथ जाता है uint32_t
और uint64_t
चूंकि आप अक्सर चर के सटीक आकार को जानना चाहते हैं। एक संकेत है कि एक विशेष चर इस इलाज के हकदार है कि आप केवल के साथ साथ इस पर काम करते है बिटवाइज़ जैसे ऑपरेटरों ~
, |
, &
, ^
, >>
और इतने पर, और नहीं अंकगणितीय आपरेशनों जैसे के साथ +
, -
, *
, /
आदि
अनसाइनड यहाँ आदर्श है क्योंकि बिटवाइज़ ऑपरेटरों का व्यवहार अच्छी तरह से परिभाषित और मानकीकृत है। हस्ताक्षर किए गए मानों में कई समस्याएं हैं, जैसे कि स्थानांतरण के समय अपरिभाषित और अनिर्दिष्ट व्यवहार और एक अनिर्दिष्ट प्रतिनिधित्व।
जब आप वास्तव में मॉड्यूलर अंकगणित चाहते हैं। कभी-कभी आप वास्तव में 2 ^ एन मॉड्यूलर अंकगणित चाहते हैं। इन मामलों में "अतिप्रवाह" एक विशेषता है, बग नहीं। अहस्ताक्षरित मान आपको वही देते हैं जो आप यहां चाहते हैं क्योंकि वे मॉड्यूलर अंकगणित का उपयोग करने के लिए परिभाषित किए गए हैं। क्योंकि वे एक अनिर्दिष्ट प्रतिनिधित्व है और अतिप्रवाह अपरिभाषित है पर हस्ताक्षरित मान (आसानी से, कुशलता से) का उपयोग नहीं किया जा सकता है।
०.५ मैंने इसे लिखने के बाद महसूस किया कि यह जैरोड के उदाहरण के लगभग समान है , जिसे मैंने नहीं देखा था - और अच्छे कारण के लिए, यह एक अच्छा उदाहरण है!
1 हम size_t
यहां बात कर रहे हैं आमतौर पर 2 ^ 32-1 एक 32-बिट सिस्टम पर या 2 ^ 64-1 64-बिट एक पर।
2 सी + + में यह बिल्कुल मामला नहीं है क्योंकि अहस्ताक्षरित मानों में संबंधित हस्ताक्षरित प्रकार की तुलना में ऊपरी छोर पर अधिक मूल्य होते हैं, लेकिन मूल समस्या मौजूद है कि अहस्ताक्षरित मानों में हेरफेर करने के परिणामस्वरूप (तार्किक रूप से) हस्ताक्षरित मान हो सकते हैं, लेकिन कोई संगत मुद्दा नहीं है। हस्ताक्षरित मूल्यों के साथ (क्योंकि हस्ताक्षरित मूल्यों में पहले से ही अहस्ताक्षरित मूल्य शामिल हैं)।
unsigned int x = 0; --x;
जोx
बनने की कोशिश करो और देखो । सीमा की जांच के बिना, आकार को अचानक कुछ अप्रत्याशित मूल्य मिल सकता है जो आसानी से यूबी तक ले जा सकता है।