क्यों (केवल) कुछ संकलक समान स्ट्रिंग शाब्दिक के लिए समान पते का उपयोग करते हैं?


92

https://godbolt.org/z/cyBiWY

मैं 'some'MSVC द्वारा उत्पन्न कोडांतरक कोड में दो शाब्दिक देख सकता हूं , लेकिन केवल एक क्लेंग और जीसीसी के साथ। यह कोड निष्पादन के पूरी तरह से अलग परिणामों की ओर जाता है।

static const char *A = "some";
static const char *B = "some";

void f() {
    if (A == B) {
        throw "Hello, string merging!";
    }
}

क्या कोई उन संकलन आउटपुट के बीच अंतर और समानता की व्याख्या कर सकता है? जब कोई अनुकूलन का अनुरोध नहीं किया जाता है तब भी क्लैंग / जीसीसी कुछ का अनुकूलन क्यों करता है? क्या यह किसी प्रकार का अपरिभाषित व्यवहार है?

मैं यह भी देखता हूं कि अगर मैं नीचे दिखाए गए घोषणाओं को बदलता हूं, तो क्लैंग / gcc / msvc "some"कोडांतरक कोड में बिल्कुल भी नहीं छोड़ता है । व्यवहार अलग क्यों है?

static const char A[] = "some";
static const char B[] = "some";

4
stackoverflow.com/a/52424271/1133179 मानक उद्धरण के साथ, एक करीबी संबंधित प्रश्न के लिए कुछ अच्छा प्रासंगिक उत्तर।
luk32


6
MSVC के लिए, / GF कंपाइलर विकल्प इस व्यवहार को नियंत्रित करता है। Docs.microsoft.com/en-us/cpp/build/reference/…
Sjoerd

1
FYI करें, यह फ़ंक्शन के लिए भी हो सकता है।
user541686

जवाबों:


109

यह अपरिभाषित व्यवहार नहीं है, लेकिन अनिर्दिष्ट व्यवहार है। के लिए स्ट्रिंग शाब्दिक ,

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

इसका मतलब है कि परिणाम या A == Bहो सकता है , जिस पर आपको निर्भर नहीं होना चाहिए।truefalse

मानक से, [lex.string] / 16 :

क्या सभी स्ट्रिंग शाब्दिक अलग-अलग हैं (जो कि नॉनओवरलैपिंग ऑब्जेक्ट में संग्रहीत हैं) और क्या स्ट्रिंग-शाब्दिक उपज का क्रमिक मूल्यांकन समान है या एक अलग वस्तु अनिर्दिष्ट है।


36

अन्य उत्तरों ने बताया कि आप सूचक के पतों के अलग होने की उम्मीद क्यों नहीं कर सकते। फिर भी आप इसे आसानी से फिर से लिख सकते हैं जो गारंटी देता है Aऔर Bबराबर की तुलना नहीं करता है:

static const char A[] = "same";
static const char B[] = "same";// but different

void f() {
    if (A == B) {
        throw "Hello, string merging!";
    }
}

अंतर यह है कि Aऔर Bअब वर्णों के सरणियों हैं। इसका मतलब है कि वे संकेत नहीं कर रहे हैं और उनके पते अलग-अलग होने चाहिए जैसे कि दो पूर्णांक चर होंगे। C ++ इसे भ्रमित करता है क्योंकि यह संकेत देता है और सरणियाँ विनिमेय प्रतीत होता है ( operator*और operator[]समान व्यवहार करने लगता है), लेकिन वे वास्तव में अलग हैं। जैसे कुछ const char *A = "foo"; A++;पूरी तरह से कानूनी है, लेकिन const char A[] = "bar"; A++;ऐसा नहीं है।

अंतर के बारे में सोचने का एक तरीका यह है char A[] = "..."कि "मुझे स्मृति का एक खंड दें और ...उसके बाद वर्णों के साथ भरें \0", जबकि char *A= "..."कहता है "मुझे एक पता दें जिस पर मैं ...उसके बाद के पात्रों को ढूंढ सकता हूं \0"।


8
यह एक बेहतर उत्तर होगा यदि आप बता सकते हैं कि यह अलग क्यों है।
मार्क रैनसम

ध्यान दें कि *pऔर p[0]केवल नहीं लेकिन परिभाषा द्वारा "एक ही व्यवहार करने लगते हैं" कर रहे हैं समान (बशर्ते कि p+0 == pएक पहचान संबंध है, क्योंकि 0सूचक-पूर्णांक अलावा तटस्थ तत्व है)। सब के बाद, p[i]के रूप में परिभाषित किया गया है *(p+i)। उत्तर हालांकि एक अच्छा बिंदु बनाता है।
पीटर - मोनिका

typeof(*p)और typeof(p[0])दोनों हैं charइसलिए वास्तव में बहुत कुछ नहीं बचा है जो अलग हो सकता है। मैं इस बात से सहमत हूं कि 'समान व्यवहार करना' सबसे अच्छा शब्द नहीं है, क्योंकि शब्दार्थ इतने अलग हैं। आपका पोस्ट सी ++ सरणियों के पहुँच तत्वों के लिए सबसे अच्छा तरीका है की मुझे याद दिलाया: 0[p], 1[p], 2[p]आदि इस तरह पेशेवरों यह करते हैं, कम से कम जब वे लोग हैं, जो सी प्रोग्रामिंग भाषा के बाद पैदा हुए थे भ्रमित करने के लिए चाहते हैं।
tobi_s


यह दिलचस्प है, और मुझे C FAQ में एक लिंक जोड़ने का प्रलोभन दिया गया था, लेकिन मैंने महसूस किया कि संबंधित प्रश्न बहुत हैं, लेकिन इस प्रश्न के बिंदु पर कोई भी कटौती नहीं करता है।
tobi_s

23

कंपाइलर के लिए समान स्ट्रिंग स्थान का उपयोग करना है या नहीं Aऔर Bइसे लागू करना है या नहीं। औपचारिक रूप से आप कह सकते हैं कि आपके कोड का व्यवहार अनिर्दिष्ट है

दोनों विकल्प C ++ मानक को सही ढंग से लागू करते हैं।


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

3

यह अंतरिक्ष को बचाने के लिए एक अनुकूलन है, जिसे अक्सर "स्ट्रिंग पूलिंग" कहा जाता है। यहाँ MSVC के लिए डॉक्स है:

https://msdn.microsoft.com/en-us/library/s0s0asdt.aspx

इसलिए यदि आप कमांड लाइन में / GF जोड़ते हैं तो आपको MSVC के साथ समान व्यवहार देखना चाहिए।

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

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