जीसीसी के साथ x86 पर पूर्णांक ओवरफ्लो क्यों होता है?


129

निम्न कोड GCC पर एक अनंत लूप में जाता है:

#include <iostream>
using namespace std;

int main(){
    int i = 0x10000000;

    int c = 0;
    do{
        c++;
        i += i;
        cout << i << endl;
    }while (i > 0);

    cout << c << endl;
    return 0;
}

तो यहाँ सौदा है: हस्ताक्षरित पूर्णांक अतिप्रवाह तकनीकी रूप से अपरिभाषित व्यवहार है। लेकिन x86 पूर्णांक निर्देशों का उपयोग करके x86 के जीसीसी पूर्णांक अंकगणित पर जीसीसी - जो अतिप्रवाह पर लपेटता है।

इसलिए, मैंने उम्मीद की होगी कि यह अतिप्रवाह पर लिपटा रहेगा - इस तथ्य के बावजूद कि यह अपरिभाषित व्यवहार है। लेकिन यह स्पष्ट रूप से ऐसा नहीं है। तो, मैंने क्या खोया?

मैंने इसका उपयोग करके संकलित किया:

~/Desktop$ g++ main.cpp -O2

GCC आउटपुट:

~/Desktop$ ./a.out
536870912
1073741824
-2147483648
0
0
0

... (infinite loop)

ऑप्टिमाइज़ेशन अक्षम होने के साथ, कोई अनंत लूप नहीं है और आउटपुट सही है। विजुअल स्टूडियो भी इसे सही ढंग से संकलित करता है और निम्न परिणाम देता है:

सही आउटपुट:

~/Desktop$ g++ main.cpp
~/Desktop$ ./a.out
536870912
1073741824
-2147483648
3

यहाँ कुछ अन्य विविधताएँ हैं:

i *= 2;   //  Also fails and goes into infinite loop.
i <<= 1;  //  This seems okay. It does not enter infinite loop.

यहां सभी प्रासंगिक संस्करण जानकारी दी गई है:

~/Desktop$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ..

...

Thread model: posix
gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4) 
~/Desktop$ 

तो सवाल यह है कि क्या यह जीसीसी में बग है? या मुझे कुछ गलत समझ में आया कि जीसीसी पूर्णांक अंकगणित को कैसे संभालता है?

* मैं इस सी को भी टैग कर रहा हूं, क्योंकि मुझे लगता है कि यह बग सी में पुन: उत्पन्न होगा (मैंने अभी तक इसे सत्यापित नहीं किया है।)

संपादित करें:

यहाँ लूप की असेंबली है: (यदि मैंने इसे ठीक से पहचाना है)

.L5:
addl    %ebp, %ebp
movl    $_ZSt4cout, %edi
movl    %ebp, %esi
.cfi_offset 3, -40
call    _ZNSolsEi
movq    %rax, %rbx
movq    (%rax), %rax
movq    -24(%rax), %rax
movq    240(%rbx,%rax), %r13
testq   %r13, %r13
je  .L10
cmpb    $0, 56(%r13)
je  .L3
movzbl  67(%r13), %eax
.L4:
movsbl  %al, %esi
movq    %rbx, %rdi
addl    $1, %r12d
call    _ZNSo3putEc
movq    %rax, %rdi
call    _ZNSo5flushEv
cmpl    $3, %r12d
jne .L5

10
यदि आप जनरेट किए गए असेंबली कोड को इसमें शामिल करते हैं तो यह बहुत अधिक जवाबदेह होगा gcc -S
ग्रेग हेविल

विधानसभा आश्चर्यजनक रूप से लंबी है। क्या मुझे अभी भी इसे संपादित करना चाहिए?
मिस्टिक ऑक्ट

बस अपने पाश के लिए प्रासंगिक भागों, कृपया।
ग्रेग हेविल

12
-1। आप कहते हैं कि यह सख्ती से अपरिभाषित व्यवहार बोल रहा है और पूछ रहा है कि क्या यह अपरिभाषित व्यवहार है। इसलिए यह मेरे लिए एक वास्तविक प्रश्न नहीं है।
जोहान्स शाउब -

8
@ जोहान्सचैब-लिट टिप्पणी के लिए धन्यवाद। शायद मेरी ओर से बुरा शब्द। मैं आपकी पूरी कोशिश करूँगा कि आप अपने अनडाउट कमाने के तरीके को स्पष्ट करें (और मैं उसी के अनुसार प्रश्न संपादित करूँगा)। असल में, मुझे पता है कि यह यूबी है। लेकिन मुझे यह भी पता है कि x86 पर जीसीसी x86 पूर्णांक निर्देशों का उपयोग करता है - जो अतिप्रवाह पर लपेटते हैं। इसलिए, मुझे उम्मीद थी कि यूबी होने के बावजूद इसे लपेटना होगा। हालाँकि, यह नहीं था और इसने मुझे भ्रमित कर दिया। इसलिए सवाल।
रहस्यपूर्ण

जवाबों:


178

जब मानक कहता है कि यह अपरिभाषित व्यवहार है, तो इसका मतलब है । कुछ भी हो सकता है। "कुछ भी" शामिल है "आमतौर पर पूर्णांक चारों ओर लपेटते हैं, लेकिन अवसर पर अजीब सामान होता है"।

हां, x86 CPU पर, पूर्णांक आमतौर पर आपकी अपेक्षा के अनुरूप होते हैं। यह उन अपवादों में से एक है। संकलक मानता है कि आप अपरिभाषित व्यवहार का कारण नहीं बनेंगे, और लूप परीक्षण को दूर कर सकते हैं। यदि आप वास्तव में रैपराउंड चाहते हैं, तो पास -fwrapvकरें g++या gccजब संकलन करें; यह आपको अच्छी तरह से परिभाषित (दो-पूरक) अतिप्रवाह शब्दार्थ प्रदान करता है, लेकिन प्रदर्शन को चोट पहुंचा सकता है।


24
ओह वाह। मुझे इसकी जानकारी नहीं थी -fwrapv। इस पर ध्यान दिलाने के लिए धन्यवाद।
रहस्यपूर्ण

1
क्या कोई चेतावनी विकल्प है जो आकस्मिक अनंत छोरों को नोटिस करने का प्रयास करता है?
जेफ बर्ड्स

5
मैंने पाया -Wunsafe- लूप-अनुकूलन यहाँ उल्लेख किया गया है: stackoverflow.com/questions/2982507/…
जेफ बर्गेज

1
-1 "हां, x86 सीपीयू पर, पूर्णांक आमतौर पर आपके द्वारा अपेक्षित तरीके से लपेटते हैं।" यह गलत है। लेकिन यह सूक्ष्म है। जैसा कि मुझे याद है कि यह उन्हें अतिप्रवाह पर फंसाने के लिए संभव है, लेकिन यही वह नहीं है जिसके बारे में हम यहां बात कर रहे हैं , और मैंने कभी ऐसा नहीं किया है। इसके अलावा, और x86 बीसीडी ऑपरेशन (सी ++ में प्रतिनिधित्व की अनुमति नहीं) की अवहेलना करते हुए x86 पूर्णांक ऑप्स हमेशा लपेटते हैं, क्योंकि वे दो के पूरक हैं। आप x86 पूर्णांक ऑप्स की संपत्ति के लिए जी ++ दोषपूर्ण (या बेहद अव्यवहारिक और बकवास) अनुकूलन को गलत समझ रहे हैं।
चीयर्स एंड हीथ। - अल्फ

5
@ Cheersandhth.-Alf, पर 'x86 CPUs' से मेरा मतलब है 'जब आप एक C कंपाइलर का उपयोग करके x86 CPUs के लिए विकसित कर रहे हैं।' क्या मुझे वास्तव में इसे वर्तनी की आवश्यकता है? यदि आप कोडांतरक में विकसित हो रहे हैं, तो स्पष्ट रूप से कंपाइलर और जीसीसी के बारे में मेरी सारी बात अप्रासंगिक है, इस मामले में पूर्णांक अतिप्रवाह के लिए शब्दार्थ वास्तव में बहुत अच्छी तरह से परिभाषित हैं।
2

18

यह सरल है: अपरिभाषित व्यवहार - विशेष रूप से अनुकूलन के साथ ( -O2) चालू - मतलब कुछ भी हो सकता है।

आपका कोड -O2स्विच के बिना अपेक्षित (आप) व्यवहार करता है ।

यह रास्ते से icl और tcc के साथ काफी ठीक काम करता है, लेकिन आप इस तरह से सामान पर भरोसा नहीं कर सकते ...

के अनुसार इस , जीसीसी अनुकूलन वास्तव में अतिप्रवाह पूर्णांक हस्ताक्षर किए दोहन करता है। इसका मतलब यह होगा कि "बग" डिजाइन द्वारा है।


यह थोड़े बेकार है कि एक कंपाइलर अपरिभाषित व्यवहार के लिए सभी चीजों के अनंत लूप का विकल्प चुन लेगा।
उलटा

27
@ कविता: मैं असहमत हूं। यदि आपके पास अपरिभाषित व्यवहार के साथ कुछ कोडित है, तो एक अनंत लूप के लिए प्रार्थना करें। यह पता लगाने में आसान बनाता है ...
डेनिस

मेरा मतलब है कि यदि संकलक सक्रिय रूप से यूबी की तलाश में है, तो टूटे हुए कोड को हाइपर-ऑप्टिमाइज़ करने की कोशिश करने के बजाय अपवाद क्यों न डालें?
उलटा

15
@ इनवर्स: कंपाइलर सक्रिय रूप से अपरिभाषित व्यवहार की तलाश में नहीं है , यह मानता है कि यह घटित नहीं होता है। इससे कंपाइलर कोड को ऑप्टिमाइज़ कर सकता है। उदाहरण के लिए, कंप्यूटिंग के बजाय for (j = i; j < i + 10; ++j) ++k;, यह बस सेट हो जाएगा k = 10, क्योंकि यह हमेशा सच होगा यदि कोई हस्ताक्षरित अतिप्रवाह नहीं होता है।
डेनिस

@ पीछे संकलक ने किसी भी चीज़ के लिए "विकल्प" नहीं चुना। आपने अपने कोड में लूप लिखा। संकलक ने इसका आविष्कार नहीं किया।
9

13

यहां ध्यान देने वाली महत्वपूर्ण बात यह है कि C ++ प्रोग्राम C ++ एब्सट्रैक्ट मशीन (जो आमतौर पर हार्डवेयर निर्देशों के माध्यम से अनुकरण किया जाता है) के लिए लिखा जाता है। तथ्य यह है कि आप x86 के लिए संकलन कर रहे हैं इस तथ्य के लिए पूरी तरह से अप्रासंगिक है कि यह अपरिभाषित व्यवहार है।

संकलक अपनी आशाओं को बेहतर बनाने के लिए अपरिभाषित व्यवहार के अस्तित्व का उपयोग करने के लिए स्वतंत्र है, (इस उदाहरण में, एक लूप से एक सशर्त को हटाकर)। कोई गारंटी नहीं है, या उपयोगी भी नहीं है, C ++ स्तर के निर्माण और x86 स्तर के मशीन कोड के बीच मानचित्रण आवश्यकता से अलग है कि मशीन कोड, निष्पादित होने पर, C ++ सार मशीन द्वारा मांगे गए परिणाम का उत्पादन करेगा।


5
i += i;

// अतिप्रवाह अपरिभाषित है।

With -wwrapv यह सही है। -fwrapv


3

कृपया लोगों को, अपरिभाषित व्यवहार बिल्कुल, अपरिभाषित है । इसका मतलब है कि कुछ भी हो सकता है। व्यवहार में (इस मामले में), संकलक यह मानने के लिए स्वतंत्र है कि यह नहीं होगाकहा जाता है, और अगर यह कोड तेज / छोटा कर सकता है जो कुछ भी यह काम करता है। कोड के साथ क्या होता है जो नहीं चलना चाहिए किसी का अनुमान है। यह आसपास के कोड (उस पर निर्भर करता है, संकलक अच्छी तरह से अलग कोड उत्पन्न कर सकता है) पर निर्भर करता है, चर / स्थिरांक का उपयोग किया जाता है, संकलक झंडे, ... ओह, और संकलक अद्यतन हो सकते हैं और एक ही कोड को अलग तरह से लिख सकते हैं, या आप कर सकते हैं कोड पीढ़ी पर एक अलग दृष्टिकोण के साथ एक और संकलक प्राप्त करें। या बस एक अलग मशीन प्राप्त करें, यहां तक ​​कि एक ही आर्किटेक्चर लाइन में एक और मॉडल बहुत अच्छी तरह से हो सकता है यह स्वयं अपरिभाषित व्यवहार है (अपरिभाषित ऑपकोड देखें, कुछ उद्यमी प्रोग्रामर ने पाया कि उन शुरुआती मशीनों में से कुछ पर कभी-कभी उपयोगी कार्य किया था ...) । नहीं है कोई"संकलक अपरिभाषित व्यवहार पर एक निश्चित व्यवहार देता है"। ऐसे क्षेत्र हैं जो कार्यान्वयन-परिभाषित हैं, और वहां आपको लगातार व्यवहार करने वाले कंपाइलर पर भरोसा करने में सक्षम होना चाहिए।


1
हां, मैं अच्छी तरह से जानता हूं कि अपरिभाषित व्यवहार क्या है। लेकिन जब आप जानते हैं कि किसी विशेष वातावरण के लिए भाषा के कुछ पहलुओं को कैसे लागू किया जाता है, तो आप कुछ प्रकार के यूबी और अन्य नहीं देखने की अपेक्षा कर सकते हैं। मुझे पता है कि जीसीसी पूर्णांक अंकगणित को x86 पूर्णांक अंकगणित के रूप में लागू करता है - जो अतिप्रवाह पर लपेटता है। इसलिए मैंने इस तरह के व्यवहार को मान लिया। मुझे उम्मीद नहीं थी कि जीसीसी कुछ और करने के लिए जीएससी के रूप में जवाब दे चुका है।
रहस्यपूर्ण

7
गलत। क्या होता है कि जीसीसी को यह मानने की अनुमति है कि आप अपरिभाषित व्यवहार को लागू नहीं करेंगे, इसलिए यह सिर्फ कोड का उत्सर्जन करता है जैसे कि ऐसा नहीं हो सकता। यदि ऐसा होता है, तो बिना किसी अपरिभाषित व्यवहार के साथ जो आप करने के लिए कहते हैं उसे निष्पादित करने के निर्देश मिलते हैं, और सीपीयू जो भी करता है उसका परिणाम होता है। यानी, x86 पर x86 सामान है। यदि यह एक और प्रोसेसर है, तो यह पूरी तरह से कुछ अलग कर सकता है। या कंपाइलर यह पता लगाने के लिए पर्याप्त स्मार्ट हो सकता है कि आप अपरिभाषित व्यवहार पर कॉल कर रहे हैं और नेटहॉक शुरू करते हैं (हाँ, जीसीसी के कुछ प्राचीन संस्करणों ने ठीक यही किया था)।
वॉनब्रांड

4
मेरा मानना ​​है कि आपने मेरी टिप्पणी को गलत बताया। मैंने कहा: "मुझे क्या उम्मीद नहीं थी" - यही वजह है कि मैंने पहली जगह में सवाल पूछा। मुझे उम्मीद नहीं थी कि जीसीसी कोई चाल खींचेगा।
15

1

यहां तक ​​कि अगर एक कंपाइलर को यह निर्दिष्ट करना था कि पूर्णांक ओवरफ्लो को अपरिभाषित व्यवहार का "गैर-महत्वपूर्ण" रूप माना जाना चाहिए (जैसा कि अनुलग्नक एल में परिभाषित किया गया है), एक पूर्णांक अतिप्रवाह का परिणाम होना चाहिए, एक विशिष्ट मंच को और अधिक विशिष्ट व्यवहार का वादा करना चाहिए, हो कम से कम "आंशिक रूप से अनिश्चित मान" के रूप में माना जाता है। इस तरह के नियमों के तहत, 1073741824 + 1073741824 को मनमाने ढंग से 2147483648 या -2147483648 या किसी भी अन्य मूल्य के रूप में माना जा सकता है, जो 2147483643 मॉड 4294967296 के अनुरूप था, और परिवर्धन द्वारा प्राप्त मूल्यों को किसी भी मूल्य के रूप में माना जा सकता है जो कि बधाई हो।

"आंशिक रूप से अनिश्चित-अनिश्चित मूल्यों" के लिए अतिप्रवाह की अनुमति देने वाले नियमों को अनुलग्नक एल के पत्र और भावना का पालन करने के लिए पर्याप्त रूप से अच्छी तरह से परिभाषित किया जाएगा, लेकिन एक कंपाइलर को आम तौर पर उपयोगी इंफ़ेक्शन बनाने से नहीं रोका जाएगा क्योंकि ओवरफ्लो अनियंत्रित थे अपरिभाषित व्यवहार। यह कुछ संकलक को "अनुकूलन" बनाने से एक कंपाइलर को रोक देगा, जिसका कई मामलों में प्राथमिक प्रभाव यह है कि प्रोग्रामर को कोड में अतिरिक्त अव्यवस्था की आवश्यकता होती है जिसका एकमात्र उद्देश्य ऐसी "अनुकूलन" को रोकना है; यह अच्छी बात होगी या नहीं यह किसी की बात पर निर्भर करता है।

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