यदि कोई संख्या बहुत बड़ी है तो वह अगली मेमोरी लोकेशन पर फैल जाती है?


30

मैं सी प्रोग्रामिंग की समीक्षा कर रहा हूं और मुझे परेशान करने वाली कुछ चीजें हैं।

इस कोड को उदाहरण के लिए लेते हैं:

int myArray[5] = {1, 2, 2147483648, 4, 5};
int* ptr = myArray;
int i;
for(i=0; i<5; i++, ptr++)
    printf("\n Element %d holds %d at address %p", i, myArray[i], ptr);

मुझे पता है कि एक int अधिकतम 2,147,483,647 धनात्मक मान रख सकता है। तो उस पर जाकर, यह अगले स्मृति पते पर "फैल" करता है जो तत्व 2 को उस पते पर "-2147483648" के रूप में प्रदर्शित करता है? लेकिन तब यह वास्तव में समझ में नहीं आता क्योंकि आउटपुट में यह अभी भी कहता है कि अगला पता 4 का मान रखता है, फिर 5. यदि नंबर अगले पते पर दिया गया था, तो उस पते पर संग्रहीत मान को नहीं बदलेगा ?

मुझे एमआइपी असेंबली में प्रोग्रामिंग से याद है और पते को प्रोग्राम चरण के दौरान मूल्यों को बदलते हुए देखते हैं कि उन पते पर असाइन किए गए मान बदल जाएंगे।

जब तक मैं गलत तरीके से याद नहीं कर रहा हूं तब तक यहां एक और सवाल है: यदि किसी विशिष्ट पते पर निर्दिष्ट संख्या प्रकार से बड़ी है (जैसे myArray [2]) तो क्या यह बाद के पते पर संग्रहीत मूल्यों को प्रभावित नहीं करता है?

उदाहरण: हमारे पास 0x10010000 पते पर int myNum = 4 बिलियन है। बेशक myNum 4 बिलियन स्टोर नहीं कर सकता है इसलिए यह उस पते पर कुछ नकारात्मक संख्या के रूप में प्रकट होता है। इस बड़ी संख्या को संग्रहीत करने में सक्षम नहीं होने के बावजूद, 0x10010004 के बाद के पते पर संग्रहीत मूल्य पर इसका कोई प्रभाव नहीं है। सही बात?

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

माफ करना अगर मैं जहाज पर चला गया। मैं इस से पूरे दिन एक प्रमुख मस्तिष्क गोज़ रहा है।


10
आप स्ट्रिंग ओवररन से भ्रमित हो सकते हैं ।
रोबी डी

19
होमवर्क: संशोधित एक सरल सीपीयू इतना है कि यह करता है फैल। आप देखेंगे कि तर्क एक "सुविधा" के लिए कहीं अधिक जटिल हो जाता है, जो पहली बार में उपयोगी होने के बिना हर जगह सुरक्षा छेद की गारंटी देगा।
फ़िहाग

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

1
एक साधारण परीक्षण लिखो, मैं सी प्रोग्रामर नहीं हूं, लेकिन कुछ की तर्ज पर int c = INT.MAXINT; c+=1;देखें और देखें कि सी क्या हुआ।
जोनाह

2
@ जॉन: समस्या यह है कि अपरिभाषित व्यवहार में अतिप्रवाह। एसी कंपाइलर उस कोड को स्पॉट कर सकता है, और यह घटा सकता है कि यह अगम्य कोड है क्योंकि यह बिना शर्त ओवरफ्लो होता है। चूंकि अगम्य कोड कोई फर्क नहीं पड़ता, इसलिए इसे समाप्त किया जा सकता है। अंतिम परिणाम: कोई कोड नहीं बचा।
7

जवाबों:


48

नहीं, यह नहीं है। C में, चर के साथ काम करने के लिए मेमोरी पतों का एक निश्चित समूह है। यदि आप 4-बाइट के साथ एक सिस्टम पर काम कर रहे हैं ints, और आप एक intचर सेट करते हैं 2,147,483,647और फिर जोड़ते हैं 1, तो चर में आमतौर पर शामिल होगा -2147483648। (अधिकांश प्रणालियों पर। व्यवहार वास्तव में अपरिभाषित है।) कोई अन्य मेमोरी लोकेशन संशोधित नहीं किया जाएगा।

संक्षेप में, संकलक आपको उस मान को असाइन नहीं करने देगा जो कि प्रकार के लिए बहुत बड़ा है। यह एक संकलक त्रुटि उत्पन्न करेगा। यदि आप इसे किसी केस से जोड़ते हैं, तो मान को छोटा कर दिया जाएगा।

एक बिटवाइज़ तरीके से देखा जाए, अगर टाइप केवल 8 बिट्स को स्टोर कर सकता है, और आप 1010101010101एक केस के साथ इसमें मूल्य को लागू करने की कोशिश करते हैं, तो आप नीचे के 8 बिट्स के साथ समाप्त हो जाएंगे, या 01010101

आपके उदाहरण में, आप जो भी करते हैं myArray[2], उसमें myArray[3]'4' शामिल होगा। कोई "स्पिल ओवर" नहीं है। आप कुछ ऐसा करने की कोशिश कर रहे हैं जो 4-बाइट्स से अधिक है, यह उच्च अंत पर सब कुछ बंद कर देगा, नीचे 4 बाइट्स छोड़ देगा। अधिकांश प्रणालियों पर, इसका परिणाम होगा -2147483648

एक व्यावहारिक दृष्टिकोण से, आप बस यह सुनिश्चित करना चाहते हैं कि कभी ऐसा न हो। ओवरफ्लो के इस प्रकार के परिणाम अक्सर मुश्किल-से-हल करने वाले दोष होते हैं। दूसरे शब्दों में, यदि आपको लगता है कि आपके सभी मूल्यों में कोई भी मौका अरबों में होगा, तो उपयोग न करें int


52
यदि आप 4-बाइट ints के साथ एक सिस्टम पर काम कर रहे हैं, और आप 2,147,483,647 पर एक अंतर चर सेट करते हैं और फिर 1 जोड़ते हैं, तो चर में -2147483648 शामिल होंगे। => नहीं , यह अपरिभाषित व्यवहार है , इसलिए यह चारों ओर लूप हो सकता है या यह पूरी तरह से कुछ और कर सकता है; मैंने कंपाइलरों को ओवरफ्लो की अनुपस्थिति के आधार पर चेक का अनुकूलन करते हुए देखा है और उदाहरण के लिए अनंत लूप मिला है ...
Matthieu M.

क्षमा करें, हाँ, आप सही हैं। मुझे वहां "आम तौर पर" जोड़ना चाहिए था।
रोबोट

@MatthieuM एक भाषा के नजरिए से, यह सच है। किसी दिए गए सिस्टम पर निष्पादन के संदर्भ में, जो हम यहां बात कर रहे हैं, वह निरर्थक है।
hobbs

@hobbs: समस्या यह है कि जब संकलक अपरिभाषित व्यवहार के कारण कार्यक्रम को नियंत्रित करते हैं, तो वास्तव में कार्यक्रम चलाने से स्मृति के अधिलेखित होने के प्रभाव में तुलनीय व्यवहार होगा।
मथिउ एम।

24

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

हालाँकि, अहस्ताक्षरित पूर्णांक ओवरफ़्लो अच्छी तरह से परिभाषित है। यह modulo UINT_MAX + 1 को लपेटेगा। आपके चर द्वारा अधिग्रहित की गई मेमोरी प्रभावित नहीं होगी।

Https://stackoverflow.com/q/18195715/951890 भी देखें


हस्ताक्षरित पूर्णांक अतिप्रवाह बस के रूप में अच्छी तरह से परिभाषित है अहस्ताक्षरित पूर्णांक अतिप्रवाह है। यदि शब्द में $ N $ बिट्स हैं, तो हस्ताक्षरित पूर्णांक ओवरफ़्लो की ऊपरी सीमा $ $ 2 ^ {N-1} -1 $ $ (जहां यह लगभग $ -2 ^ {N-1} $ के लिए लपेटता है) है, जबकि अहस्ताक्षरित पूर्णांक अतिप्रवाह के लिए ऊपरी सीमा $ $ 2 ^ N - 1 $ $ है (जहां यह लगभग $ 0 $ में लपेटता है)। इसके अलावा और घटाव के लिए एक ही तंत्र, संख्याओं की एक ही आकार ($ 2 ^ एन $) जिसका प्रतिनिधित्व किया जा सकता है। बस अतिप्रवाह की एक अलग सीमा।
रॉबर्ट ब्रिस्टो-जॉनसन

1
@ रोबर्टब्रिस्टो-जॉनसन: सी मानक के अनुसार नहीं।
वॉन काटो

ठीक है, मानक कभी-कभी एनेक्रोनॉस्टिक होते हैं। एसओ संदर्भ को देखते हुए, एक टिप्पणी है जो इसे सीधे हिट करती है: "यहां महत्वपूर्ण नोट, हालांकि, यह है कि आधुनिक दुनिया में 2 के पूरक हस्ताक्षरित अंकगणित के अलावा और कुछ का उपयोग करते हुए आधुनिक दुनिया में कोई आर्किटेक्चर नहीं है। भाषा मानक अभी भी कार्यान्वयन की अनुमति देते हैं। उदाहरण के लिए, पीडीपी -1 एक शुद्ध ऐतिहासिक कलाकृति है। - एंडी रॉस अगस्त 12 '13 20:12 पर "
रॉबर्ट ब्रिस्टो-जॉनसन

मुझे लगता है कि यह सी मानक में नहीं है, लेकिन मुझे लगता है कि एक कार्यान्वयन हो सकता है जहां नियमित बाइनरी अंकगणित का उपयोग नहीं किया जाता है int। मुझे लगता है कि वे ग्रे कोड या बीसीडी या EBCDIC का उपयोग कर सकते हैं । dunno क्यों कोई भी हार्डवेयर को ग्रे कोड या EBCDIC के साथ अंकगणित करने के लिए डिज़ाइन करेगा, लेकिन फिर, मुझे पता है कि कोई भी unsignedबाइनरी के साथ क्यों करेगा और int2 के पूरक के अलावा किसी अन्य चीज़ के साथ हस्ताक्षर करेगा ।
रॉबर्ट ब्रिस्टो-जॉनसन

14

तो, यहाँ दो चीजें हैं:

  • भाषा स्तर: C के शब्दार्थ क्या हैं
  • मशीन स्तर: असेंबली के शब्दार्थ क्या हैं / सीपीयू जो आप उपयोग करते हैं

भाषा स्तर पर:

सी में:

  • ओवरफ्लो और अंडरफ्लो को अहस्ताक्षरित पूर्णांकों के लिए मॉडुलो अंकगणित के रूप में परिभाषित किया गया है , इस प्रकार उनका मूल्य "लूप्स" है।
  • ओवरफ़्लो और अंडरफ़्लो हस्ताक्षरित पूर्णांकों के लिए अपरिभाषित व्यवहार हैं, इस प्रकार कुछ भी हो सकता है

उन लोगों के लिए जो "कुछ भी" उदाहरण चाहते हैं, मैंने देखा है:

for (int i = 0; i >= 0; i++) {
    ...
}

में बदलना:

for (int i = 0; true; i++) {
    ...
}

और हां, यह एक वैध परिवर्तन है।

इसका मतलब है कि कुछ अजीब संकलक परिवर्तन के कारण अतिप्रवाह पर ओवरराइटिंग मेमोरी के संभावित जोखिम हैं।

नोट: -fsanitize=undefinedडिबग में क्लैग या gcc का उपयोग अनिर्धारित व्यवहार संहितकर्ता को सक्रिय करने के लिए जो हस्ताक्षरित पूर्णांक के अंडरफ़्लो / अतिप्रवाह पर गर्भपात करेगा।

या इसका मतलब है कि आप किसी सरणी में अनुक्रमणिका (अनियंत्रित) के लिए ऑपरेशन के परिणाम का उपयोग करके मेमोरी को ओवरराइट कर सकते हैं। यह दुर्भाग्य से अतिप्रवाह / अतिप्रवाह का पता लगाने की अनुपस्थिति में कहीं अधिक संभावना है।

नोट: एड्रेस सैनिटाइज़र-fsanitize=address को सक्रिय करने के लिए डीबग में क्लैंग या जीसीसी उपयोग पर जो कि आउट-ऑफ-बाउंड्स एक्सेस पर गर्भपात करेगा।


मशीन स्तर पर :

यह वास्तव में आपके द्वारा उपयोग किए जाने वाले विधानसभा निर्देशों और CPU पर निर्भर करता है:

  • x86 पर, ADD ओवरफ्लो / अंडरफ्लो पर 2-पूरक का उपयोग करेगा, और OF (ओवरफ्लो फ्लैग) सेट करेगा
  • भविष्य के सीपीयू सीपीयू के लिए 4 अलग-अलग ओवरफ्लो मोड होंगे Add:
    • मोडुलो: 2-पूरक मोडुलो
    • ट्रैप: एक जाल उत्पन्न होता है, अभिकलन को रोकना
    • संतृप्त: मूल्य अंडरफ्लो पर अधिकतम या अधिकतम प्रवाह पर न्यूनतम करने के लिए अटक जाता है
    • डबल चौड़ाई: परिणाम एक डबल-चौड़ाई रजिस्टर में उत्पन्न होता है

ध्यान दें कि क्या चीजें रजिस्टर या मेमोरी में होती हैं, न तो मामले में सीपीयू ओवरफ्लो पर मेमोरी को ओवरराइट करता है।


क्या अंतिम तीन मोड पर हस्ताक्षर किए गए हैं? (पहले वाले के लिए कोई फर्क नहीं पड़ता, क्योंकि यह 2-पूरक है।)
डिडुप्लिकेटर

1
@Deduplicator: मिल सीपीयू प्रोग्रामिंग मॉडल के परिचय के अनुसार , हस्ताक्षरित और अहस्ताक्षरित जोड़ के लिए अलग-अलग ऑपकोड हैं; मुझे उम्मीद है कि दोनों opcodes 4 मोड का समर्थन करेंगे (और विभिन्न बिट-चौड़ाई और स्केलर / वैक्टर पर संचालित करने में सक्षम होंगे)। फिर से, यह अब के लिए वाष्प हार्डवेयर है;)
मैथ्यू एम।

4

@ StevenBurnap के उत्तर को आगे बढ़ाने के लिए, इसका कारण यह है कि कंप्यूटर मशीन-स्तर पर कैसे काम करते हैं।

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


4

सबसे पहले (C99 मानक को मानते हुए), आप <stdint.h>मानक हेडर को शामिल करना चाहते हैं और वहां परिभाषित कुछ प्रकारों का उपयोग कर सकते हैं , विशेष रूप से int32_tजो कि 32 बिट्स के पूर्णांक पर हस्ताक्षर किए गए हैं, या uint64_tजो बिलकुल 64 बिट्स के अहस्ताक्षरित पूर्णांक है, और इसी तरह। आप int_fast16_tप्रदर्शन कारणों से प्रकारों का उपयोग करना चाह सकते हैं ।

अन्य उत्तर पढ़ें जो यह समझाते हैं कि अहस्ताक्षरित अंकगणित आसन्न मेमोरी स्थानों पर कभी नहीं फैलता है (या ओवरफ्लो होता है)। हस्ताक्षरित अतिप्रवाह पर अपरिभाषित व्यवहार से सावधान रहें ।

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

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