वर्धित GCC 6 ऑप्टिमाइज़र व्यावहारिक C ++ कोड को क्यों तोड़ता है?


148

GCC 6 में एक नया ऑप्टिमाइज़र फ़ीचर है : यह मानता है कि thisयह हमेशा अशक्त नहीं है और इसके आधार पर अनुकूलन करता है।

मान श्रेणी प्रसार अब मानता है कि C ++ सदस्य फ़ंक्शन का यह सूचक गैर-शून्य है। यह सामान्य अशक्त पॉइंटर चेक को समाप्त कर देता है, लेकिन कुछ गैर-अनुरूपता कोड-बेस (जैसे Qt-5, क्रोमियम, केडेवलप) को भी तोड़ देता है । एक अस्थायी काम के रूप में, लगभग -fno-delete-null-सूचक-चेक का उपयोग किया जा सकता है। गलत कोड को -ffanitize = अनिर्धारित के उपयोग से पहचाना जा सकता है।

परिवर्तन दस्तावेज़ स्पष्ट रूप से इसे खतरनाक बताता है क्योंकि यह बार-बार उपयोग किए जाने वाले कोड की एक आश्चर्यजनक मात्रा को तोड़ता है।

यह नई धारणा व्यावहारिक C ++ कोड को क्यों तोड़ेगी? क्या ऐसे विशेष पैटर्न हैं जहां लापरवाह या बिना सूचना के प्रोग्रामर इस विशेष अपरिभाषित व्यवहार पर भरोसा करते हैं? मैं किसी को भी लिखने की कल्पना नहीं कर सकता if (this == NULL)क्योंकि यह इतना अप्राकृतिक है।


21
@ उम्मीद है कि आप इसे अच्छे तरीके से समझेंगे। यूबी के साथ कोड को फिर से लिखा जाना चाहिए ताकि यूबी को आमंत्रित न किया जा सके। यह इतना सरल है। हेक, अक्सर पूछे जाने वाले प्रश्न हैं जो आपको बताते हैं कि इसे कैसे प्राप्त किया जाए। तो, एक वास्तविक मुद्दा नहीं IMHO। सब अच्छा।
मोनिका

49
मैं कोड में dereferencing नल बिंदुओं का बचाव करने वाले लोगों को देखकर चकित हूं। एकदम कमाल का।
सर्गेइ

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

17
मैं सर्गेई से सहमत हूं। पूरे ब्रूहा की शुरुआत इसलिए हुई क्योंकि लोग इस तथ्य पर ध्यान केंद्रित करते हैं कि thisइसे एक अंतर्निहित पैरामीटर के रूप में पारित किया गया है, इसलिए वे तब इसका उपयोग करना शुरू करते हैं जैसे कि यह एक स्पष्ट पैरामीटर था। यह। जब आप इसे शून्य मानते हैं, तो आप यूबी को वैसे ही आमंत्रित कर रहे हैं जैसे कि आपने किसी अन्य अशक्त सूचक को निष्क्रिय कर दिया हो। बस इतना ही है। यदि आप nullptrs को पास करना चाहते हैं, तो एक स्पष्ट पैरामीटर, DUH का उपयोग करें । यह कोई भी धीमा नहीं होगा, यह किसी भी प्रकार का क्लिंकर नहीं होगा, और ऐसे एपीआई वाले कोड को वैसे भी इंटर्नशिप में गहरा है, इसलिए इसमें बहुत सीमित गुंजाइश है। कहानी का अंत मुझे लगता है।
मोनिका

41
खराब कोड के चक्र को तोड़ने के लिए कुडोस से जीसीसी -> खराब कोड का समर्थन करने के लिए अक्षम्य कंपाइलर -> अधिक बुरा कोड -> अधिक अक्षम संकलन -> ...
एमएम

जवाबों:


87

मैं उस सवाल का अनुमान लगाता हूं, जिसका उत्तर देने की आवश्यकता है कि अच्छी तरह से इरादे वाले लोग पहले स्थान पर चेक क्यों लिखते हैं।

सबसे आम मामला शायद है अगर आपके पास एक वर्ग है जो स्वाभाविक रूप से होने वाली पुनरावर्ती कॉल का हिस्सा है।

अगर आप के पास था:

struct Node
{
    Node* left;
    Node* right;
};

सी में, आप लिख सकते हैं:

void traverse_in_order(Node* n) {
    if(!n) return;
    traverse_in_order(n->left);
    process(n);
    traverse_in_order(n->right);
}

C ++ में, इसे सदस्य बनाने के लिए अच्छा है:

void Node::traverse_in_order() {
    // <--- What check should be put here?
    left->traverse_in_order();
    process();
    right->traverse_in_order();
}

C ++ के शुरुआती दिनों में (मानकीकरण से पहले), इस बात पर जोर दिया गया था कि सदस्य फ़ंक्शन एक फ़ंक्शन के लिए सिंटैक्टिक चीनी थे जहां thisपैरामीटर निहित है। कोड C ++ में लिखा गया था, समकक्ष C में परिवर्तित किया गया और संकलित किया गया। ऐसे स्पष्ट उदाहरण भी थे कि thisअशक्त की तुलना सार्थक थी और मूल Cfront संकलक ने भी इसका लाभ उठाया। इसलिए C बैकग्राउंड से आना, चेक के लिए स्पष्ट विकल्प है:

if(this == nullptr) return;      

नोट: बज़्ने स्ट्रॉस्ट्रुप ने यहां तक ​​कहा कि यहाँthis के वर्षों में नियमों में बदलाव हुआ है

और इसने कई वर्षों तक कई कंपाइलरों पर काम किया। जब मानकीकरण हुआ, तो यह बदल गया। और हाल ही में, संकलक ने एक सदस्य फ़ंक्शन को कॉल करने का लाभ उठाना शुरू कर दिया, जहां अपरिभाषित व्यवहार किया thisजा रहा nullptrहै, जिसका अर्थ है कि यह स्थिति हमेशा होती है false, और संकलक इसे छोड़ने के लिए स्वतंत्र है।

इसका मतलब यह है कि इस पेड़ के किसी भी प्रकार का परिवर्तन करने के लिए, आपको या तो करने की आवश्यकता है:

  • कॉल करने से पहले सभी जांचें करें traverse_in_order

    void Node::traverse_in_order() {
        if(left) left->traverse_in_order();
        process();
        if(right) right->traverse_in_order();
    }
    

    इसका अर्थ यह भी है कि यदि आप अशक्त हो सकते हैं तो हर कॉल साइट पर जाँच करें।

  • एक सदस्य समारोह का उपयोग न करें

    इसका मतलब है कि आप पुराने सी स्टाइल कोड (शायद एक स्टैटिक विधि के रूप में) लिख रहे हैं, और इसे ऑब्जेक्ट के साथ स्पष्ट रूप से एक पैरामीटर के रूप में बुला रहे हैं। जैसे। आप कॉल साइट पर लिखने के Node::traverse_in_order(node);बजाय वापस आ गए हैं node->traverse_in_order();

  • मेरा मानना ​​है कि इस विशेष उदाहरण को ठीक करने का सबसे आसान / साफ-सुथरा तरीका यह है कि मानकों का अनुपालन वास्तव में एक के बजाय एक प्रहरी नोड का उपयोग करना है nullptr

    // static class, or global variable
    Node sentinel;
    
    void Node::traverse_in_order() {
        if(this == &sentinel) return;
        ...
    }
    

न तो पहले दो विकल्पों में से ऐसा लगता है कि अपील करना, और जबकि कोड इसके साथ दूर हो सकता है, उन्होंने this == nullptrएक उचित फिक्स का उपयोग करने के बजाय खराब कोड लिखा ।

मैं यह अनुमान लगा रहा हूँ कि इनमें से कुछ कोड आधारों की this == nullptrजाँच कैसे हुई है।


6
1 == 0अपरिभाषित व्यवहार कैसे हो सकता है? यह बस है false
जोहान्स शहाब -

11
चेक स्वयं एक अपरिभाषित व्यवहार नहीं है। यह हमेशा झूठ है, और इस तरह संकलक द्वारा समाप्त कर दिया गया है।
सर्गेइ

15
हम्म .. this == nullptrमुहावरा अपरिभाषित व्यवहार है क्योंकि आपने इससे पहले nullptr ऑब्जेक्ट पर एक सदस्य फ़ंक्शन को बुलाया है, जो अपरिभाषित है। और संकलक चेक को छोड़ने के लिए स्वतंत्र है
jtlim

6
@ जोशुआ, पहला मानक 1998 में प्रकाशित किया गया था। इससे पहले जो कुछ भी हुआ था, जो भी हर कार्यान्वयन चाहता था। आदिम युग।
सर्गेई

26
हे, वाह, मैं विश्वास नहीं कर सकता कि किसी ने कभी भी कोड लिखा है जो कॉलिंग फ़ंक्शन पर निर्भर करता है ... बिना किसी उदाहरण के । मैंने सहज रूप से "ट्रावर्स_इन_ऑर्डर को कॉल करने से पहले चेक के सभी को चिह्नित" का उपयोग किया होगा, यहां तक ​​कि thisकभी भी अशक्त होने के बारे में भी विचार किए बिना । मुझे लगता है कि शायद यह C ++ सीखने की उम्र में फायदा है जहां SO मेरे दिमाग में UB की गड़बड़ियों को रोकने के लिए मौजूद है और मुझे इस तरह से विचित्र हैक करने से रोकता है।
अंडरस्कोर_ड

65

ऐसा इसलिए होता है क्योंकि "व्यावहारिक" कोड टूट गया था और इसके साथ शुरू करने के लिए अपरिभाषित व्यवहार शामिल था। thisसूक्ष्म रूप से अनुकूलन के अलावा, एक अशक्त का उपयोग करने का कोई कारण नहीं है , आमतौर पर एक बहुत ही समय से पहले।

यह एक खतरनाक प्रथा है, क्योंकि वर्ग पदानुक्रम के कारण संकेत के समायोजन से अशक्त एक अशक्त thisमें बदल सकता है । तो, बहुत कम से कम, जिस वर्ग के तरीकों को एक अशक्त के साथ काम thisकरना चाहिए, वह आधार वर्ग के साथ एक अंतिम वर्ग होना चाहिए: यह किसी भी चीज़ से प्राप्त नहीं कर सकता है, और इसे प्राप्त नहीं किया जा सकता है। हम जल्दी से व्यावहारिक से बदसूरत-हैक-भूमि पर प्रस्थान कर रहे हैं ।

व्यावहारिक रूप से, कोड को बदसूरत नहीं होना चाहिए:

struct Node
{
  Node* left;
  Node* right;
  void process();
  void traverse_in_order() {
    traverse_in_order_impl(this);
  }
private:
  static void traverse_in_order_impl(Node * n)
    if (!n) return;
    traverse_in_order_impl(n->left);
    n->process();
    traverse_in_order_impl(n->right);
  }
};

यदि आपके पास एक खाली पेड़ था (जैसे। जड़ nullptr है), यह समाधान अभी भी अपरिवर्तित व्यवहार पर भरोसा कर रहा है, nullptr के साथ traverse_in_order को कॉल करके।

यदि पेड़ खाली है, उर्फ ​​अशक्त है Node* root, तो आप उस पर किसी भी गैर-स्थिर तरीकों को कॉल करने वाले नहीं हैं। अवधि। सी-लाइक ट्री कोड होना पूरी तरह से ठीक है जो एक स्पष्ट पैरामीटर द्वारा इंस्टेंस पॉइंटर लेता है।

यहाँ तर्क यह है कि किसी भी तरह से उन वस्तुओं पर गैर-स्थिर तरीकों को लिखने के लिए उबालने की ज़रूरत है जो एक अशक्त उदाहरण सूचक से कहे जा सकते हैं। ऐसी कोई जरूरत नहीं है। इस तरह के कोड लिखने का सी-विथ-ऑब्जेक्ट्स तरीका अभी भी सी ++ दुनिया में अच्छा है, क्योंकि यह बहुत कम से कम सुरक्षित हो सकता है। मूल रूप से, नल thisएक ऐसा माइक्रो-ऑप्टिमाइज़ेशन है, जिसमें इस तरह के संकीर्ण क्षेत्र का उपयोग किया जाता है, कि इसे अस्वीकार करना IMHO पूरी तरह से ठीक है। किसी भी सार्वजनिक एपीआई को अशक्त पर निर्भर नहीं होना चाहिए this


18
@ फिर, जिसने भी यह कोड लिखा है, वह पहले स्थान पर गलत था। यह हास्यास्पद है कि आप MFC, Qt और Chromium जैसे बहुत से टूटे हुए प्रोजेक्ट्स का नामकरण कर रहे हैं। उनके साथ अच्छा रिडक्शन।
सर्गेई

19
@, Google में भयानक कोडिंग शैली मुझे अच्छी तरह से ज्ञात है। Google कोड को कम से कम सार्वजनिक रूप से उपलब्ध (अक्सर सार्वजनिक रूप से उपलब्ध) लिखा जाता है, इसके बावजूद कि कई लोग Google कोड को चमकदार उदाहरण मानते हैं। यह हो सकता है कि यह उनकी कोडिंग शैलियों (और जब वे इस पर दिशानिर्देश हैं) को फिर से जारी करेंगे।
सर्गेई

18
@ जब कोई भी जीके का उपयोग कर संकलित क्रोमियम के साथ इन उपकरणों पर क्रोमियम की जगह नहीं ले रहा है, तो इससे पहले कि क्रोमियम जीसीसी 6 और अन्य आधुनिक संकलक का उपयोग करके संकलित किया जाएगा, इसे ठीक करने की आवश्यकता होगी। यह बहुत बड़ा काम नहीं है; thisचेकों विभिन्न स्थिर कोड विश्लेषक द्वारा उठाया जाता है, तो यह रूप में अगर किसी को मैन्युअल रूप से उन सब को नीचे शिकार करने के लिए है नहीं है। पैच शायद तुच्छ परिवर्तन की एक सौ लाइनें होगी।
मोनिका

8
@ व्यावहारिक रूप में, एक अशक्तता thisएक त्वरित दुर्घटना है। ये समस्याएँ बहुत तेज़ी से पता चलेंगी भले ही किसी को कोड पर स्थिर विश्लेषक चलाने की परवाह न हो। C / C ++ "केवल आपके द्वारा उपयोग की जाने वाली सुविधाओं के लिए भुगतान करें" मंत्र का अनुसरण करता है। यदि आप चेक चाहते हैं, तो आपको उनके बारे में स्पष्ट होना चाहिए और इसका मतलब है कि उन्हें नहीं करना this, जब बहुत देर हो चुकी है, क्योंकि कंपाइलर मान thisनहीं रहा है। अन्यथा इसे जांचना होगा this, और 99.9999% कोड के लिए ऐसे चेक समय की बर्बादी हैं।
मोनिका

10
किसी के लिए मेरी सलाह जो सोचता है कि मानक टूट गया है: एक अलग भाषा का उपयोग करें। C ++ की कोई कमी नहीं है - उन भाषाओं की तरह जिनमें अपरिभाषित व्यवहार की संभावना नहीं है।
MM

35

परिवर्तन दस्तावेज़ स्पष्ट रूप से इसे खतरनाक बताता है क्योंकि यह बार-बार उपयोग किए जाने वाले कोड की एक आश्चर्यजनक मात्रा को तोड़ता है।

दस्तावेज़ इसे खतरनाक नहीं कहता है। न ही यह दावा करता है कि यह कोड की एक आश्चर्यजनक राशि को तोड़ता है । यह केवल कुछ लोकप्रिय कोड आधारों को इंगित करता है जो यह दावा करता है कि इस अपरिभाषित व्यवहार पर भरोसा करने के लिए जाना जाता है और परिवर्तन के कारण टूट जाएगा जब तक कि वैकल्पिक विकल्प का उपयोग नहीं किया जाता है।

यह नई धारणा व्यावहारिक C ++ कोड को क्यों तोड़ देगी?

यदि व्यावहारिक c ++ कोड अपरिभाषित व्यवहार पर निर्भर करता है, तो अपरिभाषित व्यवहार में परिवर्तन इसे तोड़ सकता है। यही कारण है कि यूबी से बचा जाना है, तब भी जब उस पर भरोसा करने वाला एक कार्यक्रम उद्देश्य के अनुसार काम करता है।

क्या ऐसे विशेष पैटर्न हैं जहां लापरवाह या बिना सूचना के प्रोग्रामर इस विशेष अपरिभाषित व्यवहार पर भरोसा करते हैं?

मुझे नहीं पता कि यह व्यापक प्रसार विरोधी है , लेकिन एक अनियंत्रित प्रोग्रामर सोच सकता है कि वे अपने कार्यक्रम को दुर्घटनाग्रस्त होने से ठीक कर सकते हैं:

if (this)
    member_variable = 42;

जब वास्तविक बग एक अशक्त सूचक को कहीं और से हटा रहा है।

मुझे यकीन है कि यदि प्रोग्रामर पर्याप्त रूप से अनफ़ॉर्म है, तो वे अधिक उन्नत (एंटी) -पैटर्न के साथ आने में सक्षम होंगे जो इस यूबी पर भरोसा करते हैं।

मैं किसी को भी लिखने की कल्पना नहीं कर सकता if (this == NULL)क्योंकि यह इतना अप्राकृतिक है।

हाँ मैं।


11
"यदि व्यावहारिक सी ++ कोड अपरिभाषित व्यवहार पर निर्भर करता है, तो उस अपरिभाषित व्यवहार में परिवर्तन इसे तोड़ सकता है। यही कारण है कि यूबी से बचा जाना है" यह * 1000
अंडरस्कोर_ड

if(this == null) PrintSomeHelpfulDebugInformationAboutHowWeGotHere(); इस तरह की घटनाओं के अनुक्रम का एक अच्छा आसान-से-पढ़ा लॉग, जो एक डिबगर आसानी से आपको नहीं बता सकता है। मज़ेदार डिबगिंग करें, अब बिना घंटे खर्च किए चेक को हर जगह रखें, जब किसी बड़े डेटा सेट में अचानक यादृच्छिक नल हो, जिस कोड में आपने नहीं लिखा है ... और इस बारे में UB नियम बाद में C ++ बनाए जाने के बाद बनाया गया था। यह मान्य हुआ करता था।
स्टीफन होकेनहुल

@StephaneHockenhull यही -fsanitize=nullहै।
इरोरिका

@ user2079303 समस्याएँ: क्या यह उत्पादन कोड को उस बिंदु तक धीमा करने जा रहा है, जहां आप दौड़ते समय चेक को नहीं छोड़ सकते हैं, जिससे कंपनी को बहुत पैसा खर्च करना पड़ता है? क्या यह आकार बढ़ाने और फ्लैश में फिट नहीं है? क्या यह Atmel सहित सभी लक्ष्य प्लेटफार्मों पर काम करता है? -fsanitize=nullSPI का उपयोग करके पिन # 5,6,10,11 पर SD / MMC कार्ड में त्रुटियों को लॉग इन कर सकते हैं? यह एक सार्वभौमिक समाधान नहीं है। कुछ लोगों ने तर्क दिया है कि यह एक अशक्त वस्तु तक पहुंचने के लिए ऑब्जेक्ट-ओरिएंटेड सिद्धांतों के खिलाफ जाता है, फिर भी कुछ ओओपी भाषाओं में एक अशक्त वस्तु है, जिस पर काम किया जा सकता है, इसलिए यह ओओपी का सार्वभौमिक नियम नहीं है। 1/2
स्टीफन होकेनहुल

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

25

कुछ "व्यावहारिक" (मजाकिया तरीके से "छोटी गाड़ी") कोड जो टूट गया था, इस तरह देखा गया:

void foo(X* p) {
  p->bar()->baz();
}

और यह इस तथ्य को ध्यान में रखना भूल गया कि p->bar()कभी-कभी एक शून्य सूचक लौटाता है, जिसका अर्थ है कि कॉल करने के लिए इसे स्थगित करना baz()अपरिभाषित है।

टूटे हुए सभी कोड स्पष्ट if (this == nullptr)या if (!p) return;चेक नहीं थे। कुछ मामले केवल ऐसे कार्य थे जो किसी भी सदस्य चर का उपयोग नहीं करते थे, और इसलिए ठीक काम करने के लिए दिखाई दिए । उदाहरण के लिए:

struct DummyImpl {
  bool valid() const { return false; }
  int m_data;
};
struct RealImpl {
  bool valid() const { return m_valid; }
  bool m_valid;
  int m_data;
};

template<typename T>
void do_something_else(T* p) {
  if (p) {
    use(p->m_data);
  }
}

template<typename T>
void func(T* p) {
  if (p->valid())
    do_something(p);
  else 
    do_something_else(p);
}

इस कोड में जब आप func<DummyImpl*>(DummyImpl*)अशक्त पॉइंटर से कॉल करते हैं तो कॉल करने के लिए पॉइंटर का "वैचारिक" डीरेफेरेंस होता है p->DummyImpl::valid(), लेकिन वास्तव में वह सदस्य फ़ंक्शन falseबिना एक्सेस किए ही वापस आ जाता है *this। यह return falseइनबिल्ट हो सकता है और इसलिए व्यवहार में पॉइंटर को एक्सेस करने की आवश्यकता नहीं है। तो कुछ संकलक के साथ यह ठीक काम करता प्रतीत होता है: डेरेफ्रेंसिंग नल के लिए कोई सेगफॉल्ट नहीं है, p->valid()गलत है, इसलिए कोड कॉल do_something_else(p)करता है, जो अशक्त बिंदुओं की जांच करता है, और इसलिए कुछ भी नहीं करता है। कोई दुर्घटना या अप्रत्याशित व्यवहार नहीं देखा जाता है।

जीसीसी 6 के साथ आपको अभी भी कॉल मिलता है p->valid(), लेकिन कंपाइलर अब उस अभिव्यक्ति से प्रभावित होता है जो pगैर-अशक्त होना चाहिए (अन्यथा p->valid()अपरिभाषित व्यवहार होगा) और उस जानकारी को नोट करता है। उस अनुमान की जानकारी का उपयोग ऑप्टिमाइज़र द्वारा किया जाता है ताकि यदि कॉल do_something_else(p)इनलाइन हो जाए, तो if (p)चेक अब बेमानी माना जाता है, क्योंकि कंपाइलर को याद है कि यह शून्य नहीं है, और इसलिए कोड को इनलाइन करता है:

template<typename T>
void func(T* p) {
  if (p->valid())
    do_something(p);
  else {
    // inlined body of do_something_else(p) with value propagation
    // optimization performed to remove null check.
    use(p->m_data);
  }
}

यह अब वास्तव में एक अशक्त सूचक को निष्क्रिय करता है, और इसलिए कोड जो पहले काम करने के लिए दिखाई दिया था वह काम करना बंद कर देता है।

इस उदाहरण में बग अंदर है func, जिसे पहले अशक्त के लिए जांचना चाहिए था (या कॉल करने वालों को इसे शून्य के साथ कभी नहीं बुलाया जाना चाहिए):

template<typename T>
void func(T* p) {
  if (p && p->valid())
    do_something(p);
  else 
    do_something_else(p);
}

याद रखने के लिए एक महत्वपूर्ण बिंदु यह है कि इस तरह के अधिकांश अनुकूलन कंपाइलर का मामला नहीं है, "आह, प्रोग्रामर ने इस सूचक को अशक्त करने के लिए परीक्षण किया, मैं इसे केवल गुस्सा करने के लिए हटा दूंगा"। क्या होता है कि विभिन्न रन-ऑफ-द-मिल अनुकूलन जैसे कि इनलाइनिंग और वैल्यू रेंज प्रचार उन चेक को अतिरेक बनाने के लिए गठबंधन करते हैं, क्योंकि वे पहले के चेक या एक डीरेफेरेंस के बाद आते हैं। यदि कंपाइलर को पता है कि एक पॉइंटर एक फ़ंक्शन में बिंदु A पर गैर-शून्य है, और पॉइंटर को उसी फ़ंक्शन में बाद के बिंदु B से पहले नहीं बदला गया है, तो यह जानता है कि यह B. पर भी अशक्त है जब Inlining होता है अंक ए और बी वास्तव में कोड के टुकड़े हो सकते हैं जो मूल रूप से अलग-अलग कार्यों में थे, लेकिन अब कोड के एक टुकड़े में संयुक्त हैं, और कंपाइलर अपने ज्ञान को लागू करने में सक्षम है कि सूचक अधिक स्थानों पर गैर-अशक्त है।


क्या यह जीसीसी 6 को संकलित करने के लिए आउटपुट-टाइम-चेतावनियों के लिए संभव है जब यह इस तरह के उपयोग का सामना करता है this?
जोतिक


3
@jotik, ^ ^ ^ टीसी ने क्या कहा। यह संभव होगा, लेकिन आपको यह चेतावनी सभी कोड, सभी समय के लिए मिलेगी । मूल्य श्रेणी का प्रसार सबसे आम अनुकूलन में से एक है, जो लगभग सभी कोड को प्रभावित करता है। आशावादी लोग केवल कोड देखते हैं, जिसे सरल बनाया जा सकता है। वे "बेवकूफ द्वारा लिखे गए कोड का एक टुकड़ा नहीं देखते हैं जो चेतावनी देना चाहता है कि क्या उनका गूंगा यूबी अनुकूलित हो जाता है"। कम्पाइलर के लिए "निरर्थक जाँच जो प्रोग्रामर अनुकूलित करना चाहता है" और "निरर्थक जाँच कि प्रोग्रामर सोचता है कि मदद करेगा, लेकिन बेमानी है" के बीच अंतर बताना आसान नहीं है।
जोनाथन वेकली

1
यदि आप अपने कोड को यूबी के विभिन्न प्रकारों के लिए रनटाइम त्रुटियां देने के लिए इंस्ट्रूमेंट करना चाहते हैं, जिसमें अवैध उपयोग शामिल हैं this, तो बस उपयोग करें-fsanitize=undefined
जोनाथन वेकली


-25

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

यहाँ मैं बहुत चतुर व्यक्ति की तुलना में बहुत विस्तार से बताता हूं। (वह सी के बारे में बात कर रहा है लेकिन स्थिति वही है)।

यह हानिकारक क्यों है?

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

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

पढ़िए पूरी बात यह आपको रोने के लिए पर्याप्त है।

ठीक है, लेकिन इस बारे में क्या?

जब वापस आया, तो एक बहुत ही सामान्य मुहावरा था, जो कुछ इस तरह था:

 OPAQUEHANDLE ObjectType::GetHandle(){
    if(this==NULL)return DEFAULTHANDLE;
    return mHandle;

 }

 void DoThing(ObjectType* pObj){
     osfunction(pObj->GetHandle(), "BLAH");
 }

तो मुहावरा है: यदि pObjअशक्त नहीं है, तो आप उस हैंडल का उपयोग करते हैं, जिसमें आप डिफ़ॉल्ट हैंडल का उपयोग करते हैं। यह GetHandleफ़ंक्शन में एनकैप्सुलेटेड है ।

चाल यह है कि एक गैर-आभासी फ़ंक्शन को कॉल करने से वास्तव में thisसूचक का कोई उपयोग नहीं होता है, इसलिए कोई एक्सेस उल्लंघन नहीं है।

मैं अभी भी नहीं मिला

बहुत सारे कोड मौजूद हैं जो इस तरह लिखे गए हैं। अगर कोई बस इसे बदल देता है, बिना लाइन बदले, हर कॉल DoThing(NULL)एक दुर्घटनाग्रस्त बग है - अगर आप भाग्यशाली हैं।

यदि आप भाग्यशाली नहीं हैं, तो दुर्घटनाग्रस्त बग के कॉल दूरस्थ निष्पादन कमजोरियां बन जाते हैं।

यह अपने आप भी हो सकता है। आपको एक स्वचालित निर्माण प्रणाली मिल गई है, है ना? इसे नवीनतम संकलक में अपग्रेड करना हानिरहित है, है ना? लेकिन अब ऐसा नहीं है - यदि आपका कंपाइलर जीसीसी नहीं है।

ठीक है, तो उन्हें बताओ!

उन्हें बताया गया है। वे परिणामों के पूर्ण ज्ञान में ऐसा कर रहे हैं।

लेकिन क्यों?

कौन कह सकता है? शायद:

  • वे वास्तविक कोड पर C ++ भाषा की आदर्श शुद्धता को महत्व देते हैं
  • उनका मानना ​​है कि लोगों को मानक का पालन नहीं करने के लिए दंडित किया जाना चाहिए
  • उन्हें दुनिया की वास्तविकता की कोई समझ नहीं है
  • वे कर रहे हैं ... उद्देश्य पर बग का परिचय। शायद विदेशी सरकार के लिए। आप कहाँ रहते हैं? सभी सरकारें दुनिया के अधिकांश देशों के लिए विदेशी हैं, और अधिकांश दुनिया के कुछ से शत्रुतापूर्ण हैं।

या शायद कुछ और। कौन कह सकता है?


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

30
मैंने कहा और पूरी बात पढ़ी जैसे आपने कहा था, और वास्तव में मैं रोया था, लेकिन मुख्य रूप से फेलिक्स की मूर्खता पर जो मुझे नहीं लगता कि आप क्या पाने की कोशिश कर रहे थे ...
माइक वीन

33
बेकार शेख़ी के लिए नीचा दिखाया। "वे ... उद्देश्य पर बग का परिचय दे रहे हैं। शायद एक विदेशी सरकार के लिए।" वास्तव में? यह / r / षड्यंत्र नहीं है।
isanae

31
बार-बार निर्णय लेने वाले प्रोग्रामर मंत्र दोहराते हैं कि अपरिभाषित व्यवहार नहीं करते हैं , फिर भी ये नॉनक्स आगे बढ़ चुके हैं और वैसे भी यह कर चुके हैं। और देखो क्या हुआ। मुझे कोई सहानुभूति नहीं है। यह डेवलपर्स की गलती है, जैसा कि सरल है। उन्हें जिम्मेदारी लेने की जरूरत है। उसे याद रखो? निजी जिम्मेदारी? लोग आपके मंत्र पर भरोसा करते हैं "लेकिन व्यवहार में क्या है !" ठीक है कि यह स्थिति पहली बार कैसे उत्पन्न हुई। इस तरह की बकवास से बचना ठीक यही है कि मानक पहले स्थान पर क्यों हैं। मानकों पर कोड, और आपको कोई समस्या नहीं होगी। अवधि।
को ऑर्बिट

18
"बस पहले से काम कर रहे recompiling, संकलक के एक नए संस्करण के साथ सुरक्षित कोड सुरक्षा कमजोरियों को पेश कर सकता है" - जो हमेशा होता है । जब तक आप यह बताना नहीं चाहते हैं कि एक संकलक का एक संस्करण एकमात्र संकलक है जिसे शेष अनंत काल के लिए अनुमति दी जाएगी। क्या आपको याद है जब लिनक्स कर्नेल को केवल 2.7.2.1 gcc के साथ संकलित किया जा सकता है? जीसीसी प्रोजेक्ट भी कांटा हो गया क्योंकि लोग बुलकैप से तंग आ चुके थे। उस अतीत को पाने में लंबा समय लगा।
एमएम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.