2017-01-19 32 views
12

गलती से, मैं निम्नलिखित दिलचस्प टुकड़ा लिखा है:अजीब व्यवहार

#include <iostream> 
#include <cstring> 

size_t strlen(const char* str) { 
    std::cout << "hello"; 
    return 0; 
} 

int main() { 
    return std::strlen("sdf"); 
} 

अनपेक्षित रूप से मेरे लिए, उत्पादन "हैलो" जीसीसी 5.1 है, जो कि मेरे strlen बुलाया जा रहा है इसका मतलब है। और भी दिलचस्प, अगर मैं return हटा देता हूं, यानी std::strlen("sdf"); की कॉल के साथ मुख्य स्थानांतरित करें, तो कुछ भी मुद्रित नहीं होता है!

मैं भी बजना, जिसके लिए std::strlen असली समारोह जो स्ट्रिंग की लंबाई की गणना करता है (और कुछ भी नहीं मुद्रित हो जाता है) कॉल करने की कोशिश की। यही वह है जो मुझे देखने की उम्मीद थी।

यह कैसे समझाया जा सकता है? क्या मेरा खुद का strlen फ़ंक्शन परिभाषित करना अनिर्धारित व्यवहार माना जाता है?

+3

[आरक्षित.नाम] और [depr.c.headers] देखें। –

+0

मेरी मशीन पर आपका उदाहरण segfaults। (लिनक्स, जीसीसी-संस्करण 6.2.1 20160830) मुझे इसकी उम्मीद नहीं थी। मुझे कोड "हैलो" प्रिंट करने और ऑपरेटिंग सिस्टम में कोड 0 से बाहर निकलने की उम्मीद थी। – DusteD

+0

मुझे आपके उदाहरण (जीसीसी 5.4) के साथ एक सेगफॉल्ट भी मिलता है। मैं सुझाव दूंगा कि वैश्विक नामस्थान में 'स्ट्रेलन' फ़ंक्शन प्रदान करने के लिए यह बेहद असामान्य और "जोखिम भरा" है, क्योंकि यदि आपने स्वाभाविक रूप से ' 'पहले शामिल किया है, तो वह libc संस्करण (" सी "लिंकेज के साथ) को ओवरराइड करेगा। 'std :: strlen' को ओवरराइड करता है, जिसे लाइब्रेरी में कहीं और इस्तेमाल किया जा सकता है। उदाहरण के लिए। 'std :: cout' 'strlen' पर कॉल उत्पन्न करता प्रतीत होता है। – davmac

उत्तर

12

यहां दिलचस्प कुछ भी नहीं है, बस एक समारोह अधिभार और अपरिभाषित व्यवहार का एक सा है। आपने लाइब्रेरी फ़ंक्शन strlen() को अपने संस्करण के साथ अधिभारित किया है। चूंकि std::strlen के जीसीसी कार्यान्वयन में नामस्थान std के अंदर लाइब्रेरी फ़ंक्शन कॉल के अलावा कुछ भी नहीं है, तो आपको वह परिणाम मिल रहा है जो आप देख रहे हैं।

यहाँ cstring से प्रासंगिक अंश है:

namespace std _GLIBCXX_VISIBILITY(default) 
{ 
_GLIBCXX_BEGIN_NAMESPACE_VERSION 

    using ::strlen; 
    ... 

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

+1

क्या आप 'वापसी' हटा दिए जाने पर व्यवहार की व्याख्या कर सकते हैं? क्या कॉल सिर्फ ऑप्टिमाइज़ किया गया है (जो मुझे लगता है कि जीसीसी ऐसा कर सकता है क्योंकि यह एक मानक लाइब्रेरी फ़ंक्शन है)? – Kevin

+0

@ केविन, हाँ, लेकिन मैं जवाब में विस्तार से बताऊंगा। – SergeyA

+0

यह काम ओवरलोडिंग नहीं है। 'std :: strlen' में पैरामीटर' (char const *) 'है; एक पैरामीटर सूची के साथ एक फ़ंक्शन ओवरलोड नहीं किया जा सकता है। –

0

आप डिफ़ॉल्ट एसटीडी नाम स्थान में strlen परिभाषित है, इस प्रकार मानक एक अधिलेखित।

कारण है कि कभी कभी अपने strlen कहा जाता है और कभी कभी मानक strlen कहा जाता है, शायद इस तथ्य है कि strlen के कई प्रयोगों कार्यों के बजाय मैक्रो नहीं है से संबंधित है। इसे असेंबलर में भी लागू किया जा सकता है।

यदि यह एक मैक्रो है, तो मानक एक चलाएगा। इसके अलावा, अगर आप रिटर्न हटाते हैं, तो फ़ंक्शन कॉल को ऑप्टिमाइज़र द्वारा हटाया जा सकता है। आप -00 के साथ तुलना कर सकते हैं।

4
के अनुसार

सी ++ 14 [extern.names]/3, ::strlen आरक्षित है:

स्टैंडर्ड सी पुस्तकालय बाहरी लिंकेज के साथ घोषित से प्रत्येक नाम के साथ एक नाम के रूप में उपयोग के लिए कार्यान्वयन के लिए आरक्षित है बाहरी "सी" लिंकेज, दोनों नामस्थान std और वैश्विक नामस्थान में।

और एक आरक्षित नाम का उपयोग करने का प्रभाव, [reserved.names]/2:

एक कार्यक्रम की घोषणा की है या एक संदर्भ में, जहां यह आरक्षित है में एक नाम को परिभाषित करता है, तो के रूप में स्पष्ट रूप से अनुमति दी अन्य की तुलना में इस खंड से, इसका व्यवहार अपरिभाषित है।

तो अपने कार्यक्रम व्यवहार अपरिभाषित है।

संबंधित मुद्दे