सख्त अलियासिंग नियम क्या है?


803

सी में सामान्य अपरिभाषित व्यवहार के बारे में पूछने पर , लोग कभी-कभी सख्त अलियासिंग नियम का उल्लेख करते हैं।
उनकी बातचीत किस बारे में हो रही है?


12
@ बान वायगट: अलियासिंग नियम c ++ और c के लिए अलग-अलग हैं। यह प्रश्न क्यों cऔर के साथ टैग किया गया है c++faq
माइक एमबी

6
@ माइक: यदि आप इतिहास की जाँच करते हैं, तो आप देखेंगे कि मैंने कुछ अन्य विशेषज्ञों के मौजूदा उत्तरों के तहत प्रश्न को बदलने के प्रयास के बावजूद टैग्स को मूल रूप से बनाए रखा। इसके अलावा, भाषा-निर्भरता और संस्करण-निर्भरता "सख्त अलियासिंग नियम क्या है?" के उत्तर का एक बहुत ही महत्वपूर्ण हिस्सा है। और अंतर जानना C और C ++ के बीच कोड माइग्रेट करने वाली टीमों के लिए महत्वपूर्ण है, या दोनों में उपयोग के लिए मैक्रोज़ लिख रहा है।
बेन वोइग्ट

6
@Ben Voigt: वास्तव में - जहाँ तक मैं बता सकता हूँ - अधिकांश उत्तर केवल c से संबंधित हैं और c ++ से नहीं हैं। प्रश्न का शब्दांकन भी C- नियमों पर ध्यान केंद्रित करने का संकेत देता है (या ओपी को अभी पता नहीं था, कि अंतर है )। अधिकांश भाग के लिए नियम और सामान्य विचार समान हैं, लेकिन विशेष रूप से, जहां यूनियनों को चिंता है कि उत्तर c ++ पर लागू नहीं होते हैं। मैं थोड़ा चिंतित हूं, कि कुछ सी ++ प्रोग्रामर सख्त अलियासिंग नियम की तलाश करेंगे और यह मान लेंगे कि यहां बताई गई सभी चीजें भी सी ++ पर लागू होती हैं।
मिकएमबी

दूसरी ओर, मैं मानता हूं कि बहुत सारे अच्छे उत्तर पोस्ट किए जाने के बाद सवाल को बदलना समस्याग्रस्त है और मुद्दा वैसे भी मामूली है।
मिकएमबी

1
@ माइक: मुझे लगता है कि आप देखेंगे कि सी उत्तर स्वीकार किए गए सी पर ध्यान केंद्रित करता है, इसे सी ++ के लिए गलत बनाता है, एक तृतीय-पक्ष द्वारा संपादित किया गया था। उस हिस्से को शायद फिर से संशोधित किया जाना चाहिए।
बेन वोइग्ट

जवाबों:


562

एक विशिष्ट स्थिति जहां आप सख्त अलियासिंग समस्याओं का सामना करते हैं, जब आपके सिस्टम के शब्द आकार के बफर पर एक संरचना (एक उपकरण / नेटवर्क संदेश की तरह) का ओवरलेइंग होता है (जैसे एक संकेतक जैसे uint32_ts या uint16_ts)। जब आप ऐसे बफर पर एक संरचना को ओवरले करते हैं, या पॉइंटर कास्टिंग के माध्यम से ऐसी संरचना पर एक बफर आप आसानी से सख्त अलियासिंग नियमों का उल्लंघन कर सकते हैं।

इसलिए इस तरह के सेटअप में, अगर मैं कुछ संदेश भेजना चाहता हूं, तो मुझे दो असंगत बिंदुओं को एक ही समय में याद रखना होगा। मैं तब कुछ इस तरह कोड कर सकता हूं (सिस्टम के साथ sizeof(int) == 2):

typedef struct Msg
{
    unsigned int a;
    unsigned int b;
} Msg;

void SendWord(uint32_t);

int main(void)
{
    // Get a 32-bit buffer from the system
    uint32_t* buff = malloc(sizeof(Msg));

    // Alias that buffer through message
    Msg* msg = (Msg*)(buff);

    // Send a bunch of messages    
    for (int i =0; i < 10; ++i)
    {
        msg->a = i;
        msg->b = i+1;
        SendWord(buff[0]);
        SendWord(buff[1]);   
    }
}

सख्त अलियासिंग नियम इस सेटअप को गैरकानूनी बनाता है: एक पॉइंटर को डीरेफेरेंस करते हुए कि एलियास एक ऐसी वस्तु है जो एक संगत प्रकार की नहीं है या सी 2011 6.5 पैरा 7 1 द्वारा अनुमत अन्य प्रकारों में से एक अपरिभाषित व्यवहार है। दुर्भाग्य से, आप अभी भी इस तरह से कोड कर सकते हैं, हो सकता है कि कुछ चेतावनी प्राप्त करें, क्या यह ठीक संकलन है, केवल अजीब अप्रत्याशित व्यवहार करने के लिए जब आप कोड चलाते हैं।

(जीसीसी अलियासिंग चेतावनी देने की अपनी क्षमता में कुछ असंगत प्रतीत होता है, कभी-कभी हमें एक दोस्ताना चेतावनी देता है और कभी-कभी नहीं।)

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

ध्यान रखें, यदि आपको लगता है कि उदाहरण से वंचित हैं, तो यह तब भी हो सकता है जब आप किसी अन्य फ़ंक्शन के लिए एक बफर पास कर रहे हों, जो आपके लिए भेज रहा है, यदि आपके पास है।

void SendMessage(uint32_t* buff, size_t size32)
{
    for (int i = 0; i < size32; ++i) 
    {
        SendWord(buff[i]);
    }
}

और इस सुविधाजनक फ़ंक्शन का लाभ उठाने के लिए हमारे पहले के लूप को फिर से लिखा

for (int i = 0; i < 10; ++i)
{
    msg->a = i;
    msg->b = i+1;
    SendMessage(buff, 2);
}

कंपाइलर SendMessage को इनलाइन करने की कोशिश करने में सक्षम या स्मार्ट नहीं हो सकता है और यह बफ़र को फिर से लोड करने या न करने का निर्णय ले सकता है या नहीं भी कर सकता है। यदि SendMessageकिसी अन्य एपीआई का हिस्सा है जिसे अलग से संकलित किया गया है, तो संभवतः उसके पास बफ़र्स की सामग्री लोड करने के निर्देश हैं। तो फिर, शायद आप C ++ में हैं और यह कुछ टेम्प्लेटेड हेडर है जो केवल कार्यान्वयन के लिए है जो कंपाइलर को लगता है कि यह इनलाइन कर सकता है। या हो सकता है कि यह केवल कुछ है जो आपने अपनी .c फ़ाइल में अपनी सुविधा के लिए लिखा है। वैसे भी अपरिभाषित व्यवहार अभी भी सुनिश्चित हो सकता है। यहां तक ​​कि जब हम जानते हैं कि हुड के तहत क्या हो रहा है, यह अभी भी नियम का उल्लंघन है, इसलिए कोई भी अच्छी तरह से परिभाषित व्यवहार की गारंटी नहीं है। तो बस एक फ़ंक्शन में लपेटकर जो हमारे शब्द को सीमांकित बफर लेता है वह जरूरी मदद नहीं करता है।

तो मैं इसके आसपास कैसे पहुंचूं?

  • एक संघ का उपयोग करें। अधिकांश कंपाइलर सख्त अलियासिंग की शिकायत किए बिना इसका समर्थन करते हैं। इसे C99 में अनुमति दी गई है और C11 में स्पष्ट रूप से अनुमति दी गई है।

    union {
        Msg msg;
        unsigned int asBuffer[sizeof(Msg)/sizeof(unsigned int)];
    };
    
  • आप अपने कंपाइलर में सख्त अलियासिंग को निष्क्रिय कर सकते हैं ( f [no-] सख्त- aliasing in gcc))

  • आप char*अपने सिस्टम के शब्द के बजाय अलियासिंग के लिए उपयोग कर सकते हैं । नियम char*(सहित signed charऔर unsigned char) के लिए एक अपवाद की अनुमति देते हैं । यह हमेशा माना जाता है कि char*अन्य प्रकार के उपनाम। हालांकि यह दूसरे तरीके से काम नहीं करेगा: इस बात की कोई धारणा नहीं है कि आपकी संरचना अलायस चार्ज़ का बफर है।

शुरुआत करने वाले सावधान रहें

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

पाद लेख

1 सी 2011 6.5 7 प्रकार के उपयोग की अनुमति देता है जो निम्न हैं:

  • वस्तु के प्रभावी प्रकार के साथ संगत एक प्रकार,
  • ऑब्जेक्ट के प्रभावी प्रकार के साथ संगत एक प्रकार का एक योग्य संस्करण,
  • एक प्रकार जो वस्तु के प्रभावी प्रकार से संबंधित हस्ताक्षरित या अहस्ताक्षरित प्रकार है,
  • एक प्रकार जो वस्तु के प्रभावी प्रकार के एक योग्य संस्करण के अनुरूप हस्ताक्षरित या अहस्ताक्षरित प्रकार है,
  • एक समग्र या संघ प्रकार जिसमें उसके सदस्यों के बीच पूर्वोक्त प्रकारों में से एक शामिल है (सहित, पुनरावर्ती, एक उपसमूह या निहित संघ का सदस्य), या
  • एक चरित्र प्रकार।

16
मुझे लगता है कि लड़ाई के बाद यह आ रहा है .. इसके बजाय unsigned char*दूर इस्तेमाल किया जा सकता है char*? मैं अंतर्निहित प्रकार के unsigned charबजाय उपयोग करना चाहता हूं क्योंकि मेरे बाइट्स पर हस्ताक्षर नहीं किए गए हैं और मैं हस्ताक्षरित व्यवहार की अजीबता नहीं चाहता (विशेष रूप से अतिप्रवाह करने के लिए wrt)charbyte
मैथ्यू एम।

30
@ मैथ्यू: साइन इन करने से उर्फ ​​नियमों में कोई फर्क नहीं पड़ता है, इसलिए इसका उपयोग unsigned char *करना ठीक है।
थॉमस ईडिंग

22
क्या पिछले किसी से लिखे गए संघ के सदस्य से पढ़ना अपरिभाषित व्यवहार नहीं है?
आर। मार्टिनो फर्नांडिस

23
बोललॉक, यह जवाब पूरी तरह से पीछे की ओर है । वह उदाहरण जो अवैध के रूप में दिखाता है, वास्तव में कानूनी है, और वह उदाहरण जो कानूनी के रूप में दिखाता है, वास्तव में अवैध है।
आर। मार्टिनो फर्नांडिस

7
आपकी uint32_t* buff = malloc(sizeof(Msg));और बाद की यूनियन unsigned int asBuffer[sizeof(Msg)];बफर घोषणाओं के अलग-अलग आकार होंगे और न ही सही है। mallocकॉल हुड के नीचे 4 बाइट संरेखण पर निर्भर है (यह क्या करते हो नहीं) और संघ 4 बार बड़ा की तुलना में यह करने की जरूरत हो जाएगा ... मैं समझता हूँ कि यह स्पष्टता के लिए है, लेकिन यह कीड़े मुझे कोई-मिलने वाली कम ...
बकवास

233

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

लेख से:

"सख्त अलियासिंग एक धारणा है, जो सी (या सी ++) कंपाइलर द्वारा बनाई गई है, जो विभिन्न प्रकारों की वस्तुओं के लिए डेरेफेरिंग पॉइंट कभी भी एक ही मेमोरी लोकेशन (यानी एक दूसरे को उर्फ) का उल्लेख नहीं करेंगे।"

इसलिए मूल रूप से यदि आपके पास int*कुछ मेमोरी की ओर इशारा है intऔर फिर आप float*उस मेमोरी को इंगित करते हैं और इसे floatनियम तोड़ने के रूप में उपयोग करते हैं । यदि आपका कोड इसका सम्मान नहीं करता है, तो संकलक के ऑप्टिमाइज़र आपके कोड को सबसे अधिक बार तोड़ देंगे।

नियम का अपवाद एक है char*, जिसे किसी भी प्रकार को इंगित करने की अनुमति है।


6
तो क्या कानूनी तौर पर 2 अलग-अलग प्रकारों के चर के साथ एक ही मेमोरी का उपयोग करने का विहित तरीका है? या हर कोई सिर्फ नकल करता है?
जिगगंजर

4
माइक एक्टन का पेज त्रुटिपूर्ण है। "एक संघ (2) के माध्यम से कास्टिंग" का हिस्सा, कम से कम, बिल्कुल गलत है; कोड वह दावा करता है कि कानूनी नहीं है।
डेवमेक

11
@davmac: C89 के लेखकों ने कभी यह इरादा नहीं किया कि यह प्रोग्रामर्स को हुप्स से कूदने के लिए मजबूर करे। मैं इस धारणा को पूरी तरह से विचित्र मानता हूं कि अनुकूलन के एकमात्र उद्देश्य के लिए मौजूद एक नियम की व्याख्या इस तरह से की जानी चाहिए कि प्रोग्रामर को कोड लिखने की आवश्यकता होती है, जो अनावश्यक रूप से इस उम्मीद में डेटा की प्रतिलिपि बनाता है कि एक ऑप्टिमाइज़र अनावश्यक कोड हटा देगा।
सुपरकैट

1
@ कुरसीगू: "यूनियनों में नहीं हो सकता"? सबसे पहले, यूनियनों का मूल / प्राथमिक उद्देश्य किसी भी तरह से अलियासिंग से संबंधित नहीं है। दूसरे, आधुनिक भाषा कल्पना स्पष्ट रूप से अलियासिंग के लिए यूनियनों का उपयोग करने की अनुमति देती है। संकलक को यह नोटिस करना आवश्यक है कि एक संघ का उपयोग किया जाता है और स्थिति का इलाज एक विशेष तरीका है।
चींटी

5
@ कुरसीगुइ: मिथ्या। सबसे पहले, यूनियनों के पीछे मूल वैचारिक विचार यह था कि किसी भी समय दिए गए यूनियन ऑब्जेक्ट में केवल एक सदस्य ऑब्जेक्ट "सक्रिय" है, जबकि अन्य बस मौजूद नहीं हैं। इसलिए, "एक ही पते पर अलग-अलग वस्तुएं नहीं हैं" जैसा कि आप मानते हैं। दूसरी बात यह है कि हर कोई उल्लंघन के बारे में बात कर रही है aliasing के बारे में है तक पहुँचने के लिए एक अलग वस्तु के रूप में एक वस्तु, के बारे में बस नहीं होने समान पते के साथ दो वस्तुओं। जब तक कोई टाइप-पैंटिंग एक्सेस नहीं है, तब तक कोई समस्या नहीं है। वह मूल विचार था। बाद में, यूनियनों के माध्यम से टाइप-पाइंटिंग की अनुमति दी गई।
चींटी

133

यह सख्त अलियासिंग नियम है, जो C ++ 03 मानक के खंड 3.10 में पाया गया है (अन्य उत्तर अच्छी व्याख्या प्रदान करते हैं, लेकिन कोई भी नियम स्वयं प्रदान नहीं करता है):

यदि कोई प्रोग्राम किसी वस्तु के संग्रहित मूल्य को निम्न प्रकारों के अलावा किसी अन्य व्यवहार के माध्यम से उपयोग करने का प्रयास करता है, तो व्यवहार अप्रभावित है:

  • वस्तु का गतिशील प्रकार,
  • ऑब्जेक्ट के गतिशील प्रकार का एक cv- योग्य संस्करण,
  • एक प्रकार जो वस्तु के गतिशील प्रकार के अनुरूप हस्ताक्षरित या अहस्ताक्षरित प्रकार है,
  • एक प्रकार जो ऑब्जेक्ट के गतिशील प्रकार के cv- योग्य संस्करण के अनुरूप हस्ताक्षरित या अहस्ताक्षरित प्रकार है,
  • एक समुच्चय या संघ प्रकार जिसमें उसके सदस्यों के बीच पूर्वोक्त प्रकार शामिल हैं (सहित, पुनरावर्ती, उपसमूह या निहित संघ के सदस्य),
  • एक प्रकार जो ऑब्जेक्ट का गतिशील प्रकार का (संभवतः cv-योग्य) बेस क्लास प्रकार है,
  • एक charया unsigned charप्रकार।

C ++ 11 और C ++ 14 शब्द (परिवर्तन पर बल दिया गया):

यदि कोई प्रोग्राम किसी वस्तु के संग्रहित मूल्य को निम्न प्रकार के व्यवहार के अलावा किसी अन्य वस्तु के ग्लव्यू के माध्यम से एक्सेस करने का प्रयास करता है , तो व्यवहार अप्रभावित है:

  • वस्तु का गतिशील प्रकार,
  • ऑब्जेक्ट के गतिशील प्रकार का एक cv- योग्य संस्करण,
  • गतिशील प्रकार के ऑब्जेक्ट के समान (जैसा कि 4.4 में परिभाषित),
  • एक प्रकार जो वस्तु के गतिशील प्रकार के अनुरूप हस्ताक्षरित या अहस्ताक्षरित प्रकार है,
  • एक प्रकार जो ऑब्जेक्ट के गतिशील प्रकार के cv- योग्य संस्करण के अनुरूप हस्ताक्षरित या अहस्ताक्षरित प्रकार है,
  • एक समग्र या संघ प्रकार जिसमें उसके तत्वों या गैर-स्थैतिक डेटा सदस्यों में एक प्रकार शामिल होता है (सहित, पुनरावर्ती, एक तत्व या उप-समूह या निहित संघ के गैर-स्थैतिक डेटा सदस्य ),
  • एक प्रकार जो ऑब्जेक्ट का गतिशील प्रकार का (संभवतः cv-योग्य) बेस क्लास प्रकार है,
  • एक charया unsigned charप्रकार।

दो परिवर्तन छोटे थे: glvalue बजाय lvalue , और कुल मिलाकर / संघ मामले का स्पष्टीकरण।

तीसरा परिवर्तन एक मजबूत गारंटी देता है (मजबूत अलियासिंग नियम को शांत करता है): इसी प्रकार की नई अवधारणा जो अब उर्फ ​​के लिए सुरक्षित है।


इसके अलावा सी शब्द (C99; ISO / IEC 9899: 1999 6.5 / 7; सटीक वही शब्दांकन ISO / IEC 9899: 2011 §6.5 ¶7 में प्रयोग किया जाता है):

एक वस्तु का भंडारित मूल्य केवल एक लैवल्यू एक्सप्रेशन द्वारा एक्सेस किया जाना चाहिए जिसमें निम्नलिखित प्रकारों में से एक है 73) या 88) :

  • वस्तु के प्रभावी प्रकार के साथ संगत एक प्रकार,
  • गुण के प्रभावी प्रकार के साथ संगत एक प्रकार का a एड संस्करण,
  • एक प्रकार जो वस्तु के प्रभावी प्रकार से संबंधित हस्ताक्षरित या अहस्ताक्षरित प्रकार है,
  • एक प्रकार जो ऑब्जेक्ट के प्रभावी प्रकार के क्वालि of एड संस्करण के अनुरूप हस्ताक्षरित या अहस्ताक्षरित प्रकार है,
  • एक समग्र या संघ प्रकार जिसमें उसके सदस्यों के बीच पूर्वोक्त प्रकारों में से एक शामिल है (सहित, पुनरावर्ती, एक उपसमूह या निहित संघ का सदस्य), या
  • एक चरित्र प्रकार।

73) या 88) इस सूची का आशय उन परिस्थितियों को निर्दिष्ट करना है जिनमें कोई वस्तु अलियास हो सकती है या नहीं।


7
बेन, जैसा कि लोग अक्सर यहां निर्देशित करते हैं, मैंने खुद को सी मानक के संदर्भ में भी जोड़ने की अनुमति दी है, पूर्णता के लिए।
कोस

1
C89 Rationale cs.technion.ac.il/users/yechiel/CS/C+draft/rationale.pdf खंड 3.3 को देखें जो इसके बारे में बात करता है।
phagon1

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

2
@ सुपरकैट: जिस तरह से संरचनाओं के लिए नियम का उल्लेख किया गया है, वास्तविक पहुंच हमेशा आदिम प्रकार की होती है। फिर आदिम प्रकार के संदर्भ के माध्यम से पहुंच कानूनी है क्योंकि प्रकार मेल खाते हैं, और युक्त संरचना प्रकार के संदर्भ के माध्यम से पहुंच कानूनी है क्योंकि विशेष रूप से अनुमति है।
बेन वोइग्ट

2
@BenVoigt: मुझे नहीं लगता कि आम प्रारंभिक अनुक्रम काम करता है जब तक कि संघ के माध्यम से पहुंच नहीं होती है। देखने के लिए goo.gl/HGOyoK देखें कि gcc क्या कर रहा है। यदि किसी सदस्य प्रकार के अंतराल (यूनियन-सदस्य-एक्सेस ऑपरेटर का उपयोग न करना) के माध्यम से यूनियन प्रकार के एक लैवल्यू को एक्सेस wow(&u->s1,&u->s2)करना कानूनी था, तो एक पॉइंटर को संशोधित करने के लिए उपयोग किए जाने पर भी कानूनी होने की आवश्यकता होगी u, और यह सबसे अनुकूलन को नकार देगा सुविधा के लिए अलियासिंग नियम बनाया गया था।
सुपरकैट

80

ध्यान दें

यह मेरे "सख्त उपनाम नियम क्या है और हम क्यों परवाह करते हैं?" लिखें।

सख्त अलियासिंग क्या है?

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

दुर्भाग्य से सख्त उर्फ ​​उल्लंघन के साथ, हम अक्सर हम जो परिणाम की उम्मीद करते हैं, वह प्राप्त करेंगे, संभावना है कि एक नए अनुकूलन के साथ एक संकलक के भविष्य के संस्करण को कोड को तोड़ दिया जाएगा जिसे हमने सोचा था कि यह वैध था। यह अवांछनीय है और यह सख्त अलियासिंग नियमों को समझने का एक सार्थक लक्ष्य है और उनका उल्लंघन करने से कैसे बचें।

हम क्यों परवाह करते हैं, इसके बारे में अधिक समझने के लिए, हम उन मुद्दों पर चर्चा करेंगे जो सख्त अलियासिंग नियमों का उल्लंघन करते समय सामने आते हैं, टाइपिंग पाइंटिंग क्योंकि टाइप पाइंटिंग में उपयोग की जाने वाली सामान्य तकनीकें अक्सर सख्त अलियासिंग नियमों का उल्लंघन करती हैं और सही ढंग से सजा कैसे टाइप करें।

प्रारंभिक उदाहरण

आइए कुछ उदाहरणों को देखें, फिर हम इस बारे में बात कर सकते हैं कि मानक (एस) क्या कहते हैं, कुछ और उदाहरणों की जांच करें और फिर देखें कि कैसे सख्त अलियासिंग से बचें और उन उल्लंघनों को पकड़ें जिनसे हम चूक गए थे। यहाँ एक उदाहरण है जो आश्चर्यजनक नहीं होना चाहिए ( लाइव उदाहरण ):

int x = 10;
int *ip = &x;

std::cout << *ip << "\n";
*ip = 12;
std::cout << x << "\n";

हम एक है पूर्णांक * स्मृति को ओर इशारा करते एक के कब्जे में पूर्णांक और यह एक वैध अलियासिंग है। ऑप्टिमाइज़र को यह मान लेना चाहिए कि आईपी ​​के माध्यम से असाइनमेंट x के कब्जे वाले मूल्य को अपडेट कर सकता है ।

अगला उदाहरण अलियासिंग दिखाता है जो अपरिभाषित व्यवहार ( जीवंत उदाहरण ) की ओर जाता है :

int foo( float *f, int *i ) { 
    *i = 1;               
    *f = 0.f;            

   return *i;
}

int main() {
    int x = 0;

    std::cout << x << "\n";   // Expect 0
    x = foo(reinterpret_cast<float*>(&x), &x);
    std::cout << x << "\n";   // Expect 0?
}

फ़ंक्शन फू में हम एक इंट * और एक फ्लोट * लेते हैं , इस उदाहरण में हम फू कहते हैं और दोनों मापदंडों को एक ही मेमोरी स्थान पर इंगित करने के लिए सेट करते हैं जिसमें इस उदाहरण में एक इंट होता है । ध्यान दें, reinterpret_cast अभिव्यक्ति का इलाज करने के लिए संकलक से कह रहा है जैसे कि यह अपने टेम्पलेट पैरामीटर द्वारा टाइप किया गया था। इस मामले में हम इसे एक्सप्रेशन & x का इलाज करने के लिए कह रहे हैं जैसे कि यह फ्लोट * था । हम दूसरे काउंट के परिणाम की उम्मीद कर सकते हैं 0 हो सकता है, लेकिन ऑप्टिमाइज़ेशन के साथ -O2 का उपयोग करके सक्षम किया जाता है, दोनों gcc और clang निम्न परिणाम उत्पन्न करते हैं:

0
1

जिसकी उम्मीद नहीं की जा सकती है लेकिन यह पूरी तरह से वैध है क्योंकि हमने अपरिभाषित व्यवहार किया है। एक नाव वैध एक उपनाम नहीं कर सकते पूर्णांक वस्तु। इसलिए अनुकूलक मान सकते हैं निरंतर 1 जब अपसंदर्भन संग्रहीत मैं के बाद से के माध्यम से एक दुकान वापसी मान हो जाएगा वैध एक को प्रभावित नहीं कर सकता पूर्णांक वस्तु। कंपाइलर एक्सप्लोरर में कोड प्लग करना यह दिखाता है कि वास्तव में क्या हो रहा है ( लाइव उदाहरण ):

foo(float*, int*): # @foo(float*, int*)
mov dword ptr [rsi], 1  
mov dword ptr [rdi], 0
mov eax, 1                       
ret

का उपयोग कर अनुकूलक प्रकार के आधार उर्फ विश्लेषण (TBAA) मान लिया गया है 1 लौटा दी जाएगी और सीधे रजिस्टर में निरंतर मूल्य बढ़ता रहता है eax वापसी मान किया जाता है जो। TBAA लोड और स्टोर को अनुकूलित करने के लिए अन्य प्रकार की अनुमति देने के बारे में भाषाओं के नियमों का उपयोग करता है। इस मामले में TBAA जानता है कि एक फ्लोट उर्फ और int नहीं कर सकता है और i के भार को दूर करता है ।

अब, नियम-पुस्तक के लिए

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

C11 मानक क्या कहता है?

C11 मानक खंड में निम्नलिखित कहते हैं 6.5 भाव पैरा 7 :

एक वस्तु का अपना संग्रहीत मूल्य केवल एक लैवल्यू एक्सप्रेशन द्वारा पहुँचा जा सकता है, जिसमें निम्न में से एक प्रकार है: 88) - एक प्रकार की वस्तु के प्रभावी प्रकार के साथ संगत,

int x = 1;
int *p = &x;   
printf("%d\n", *p); // *p gives us an lvalue expression of type int which is compatible with int

- वस्तु के प्रभावी प्रकार के साथ संगत एक प्रकार का एक योग्य संस्करण,

int x = 1;
const int *p = &x;
printf("%d\n", *p); // *p gives us an lvalue expression of type const int which is compatible with int

- एक प्रकार जो वस्तु के प्रभावी प्रकार के अनुरूप हस्ताक्षरित या अहस्ताक्षरित प्रकार है,

int x = 1;
unsigned int *p = (unsigned int*)&x;
printf("%u\n", *p ); // *p gives us an lvalue expression of type unsigned int which corresponds to 
                     // the effective type of the object

gcc / clang का एक एक्सटेंशन है और यह भी अहस्ताक्षरित int * को int * असाइन करने की अनुमति देता है, भले ही वे संगत प्रकार न हों।

- एक प्रकार जो वस्तु के प्रभावी प्रकार के एक योग्य संस्करण के अनुरूप हस्ताक्षरित या अहस्ताक्षरित प्रकार है,

int x = 1;
const unsigned int *p = (const unsigned int*)&x;
printf("%u\n", *p ); // *p gives us an lvalue expression of type const unsigned int which is a unsigned type 
                     // that corresponds with to a qualified verison of the effective type of the object

- एक समग्र या संघ प्रकार जिसमें इसके सदस्यों में से एक प्रकार शामिल है (सहित, पुनरावर्ती, एक उपसमूह या निहित संघ का सदस्य), या

struct foo {
  int x;
};

void foobar( struct foo *fp, int *ip );  // struct foo is an aggregate that includes int among its members so it can
                                         // can alias with *ip

foo f;
foobar( &f, &f.x );

- एक चरित्र प्रकार।

int x = 65;
char *p = (char *)&x;
printf("%c\n", *p );  // *p gives us an lvalue expression of type char which is a character type.
                      // The results are not portable due to endianness issues.

C ++ 17 ड्राफ्ट स्टैंडर्ड क्या कहता है

अनुभाग में C ++ 17 मसौदा मानक [basic.lval] पैराग्राफ 11 कहता है:

यदि कोई प्रोग्राम किसी वस्तु के संग्रहित मूल्य को निम्न प्रकार के व्यवहार के अलावा किसी अन्य वस्तु के ग्लव्यू तक पहुँचाने का प्रयास करता है तो व्यवहार अपरिभाषित है: 63 (11.1) - वस्तु का गतिशील प्रकार,

void *p = malloc( sizeof(int) ); // We have allocated storage but not started the lifetime of an object
int *ip = new (p) int{0};        // Placement new changes the dynamic type of the object to int
std::cout << *ip << "\n";        // *ip gives us a glvalue expression of type int which matches the dynamic type 
                                  // of the allocated object

(11.2) - वस्तु के गतिशील प्रकार का एक cv- योग्य संस्करण,

int x = 1;
const int *cip = &x;
std::cout << *cip << "\n";  // *cip gives us a glvalue expression of type const int which is a cv-qualified 
                            // version of the dynamic type of x

(11.3) - वस्तु के गतिशील प्रकार के समान (जैसा कि 7.5 में परिभाषित)

(११.४) - एक प्रकार जो वस्तु के गतिशील प्रकार के अनुरूप हस्ताक्षरित या अहस्ताक्षरित प्रकार है,

// Both si and ui are signed or unsigned types corresponding to each others dynamic types
// We can see from this godbolt(https://godbolt.org/g/KowGXB) the optimizer assumes aliasing.
signed int foo( signed int &si, unsigned int &ui ) {
  si = 1;
  ui = 2;

  return si;
}

(११.५) - एक प्रकार जो वस्तु के गतिशील प्रकार के cv- योग्य संस्करण के अनुरूप हस्ताक्षरित या अहस्ताक्षरित प्रकार है,

signed int foo( const signed int &si1, int &si2); // Hard to show this one assumes aliasing

(११.६) - एक समग्र या संघ प्रकार जिसमें इसके तत्वों या गैर-डेटा डेटा सदस्यों के बीच पूर्वोक्त प्रकारों में से एक शामिल है (सहित, पुनरावर्ती, एक तत्व या किसी उप-समूह या निहित यूनियन के गैर-स्थैतिक डेटा सदस्य),

struct foo {
 int x;
};

// Compiler Explorer example(https://godbolt.org/g/z2wJTC) shows aliasing assumption
int foobar( foo &fp, int &ip ) {
 fp.x = 1;
 ip = 2;

 return fp.x;
}

foo f; 
foobar( f, f.x ); 

(११. () - एक प्रकार जो वस्तु के गतिशील प्रकार का एक (संभवतः cv-योग्य) आधार वर्ग प्रकार है,

struct foo { int x ; };

struct bar : public foo {};

int foobar( foo &f, bar &b ) {
  f.x = 1;
  b.x = 2;

  return f.x;
}

(11.8) - एक चार, अहस्ताक्षरित चार, या एसटीडी :: बाइट प्रकार।

int foo( std::byte &b, uint32_t &ui ) {
  b = static_cast<std::byte>('a');
  ui = 0xFFFFFFFF;                   

  return std::to_integer<int>( b );  // b gives us a glvalue expression of type std::byte which can alias
                                     // an object of type uint32_t
}

वर्थ नोटिंग साइन किए गए चार्ट को ऊपर की सूची में शामिल नहीं किया गया है, यह सी से एक उल्लेखनीय अंतर है जो एक चरित्र प्रकार कहता है

टाइप पुनिंग क्या है

हम इस बिंदु पर पहुंच गए हैं और हम सोच रहे होंगे कि हम क्यों इसके लिए उपनाम देना चाहेंगे? उत्तर आमतौर पर दंड टाइप करने के लिए होता है , अक्सर उपयोग किए जाने वाले तरीके सख्त अलियासिंग नियमों का उल्लंघन करते हैं।

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

परंपरागत रूप से यह वस्तु के पते को लेते हुए पूरा किया गया है, इसे उस प्रकार के एक पॉइंटर को कास्टिंग करना है जिसे हम इसे फिर से व्याख्या करना चाहते हैं और फिर मूल्य को एक्सेस करके या अन्य शब्दों में अलियासिंग करके। उदाहरण के लिए:

int x =  1 ;

// In C
float *fp = (float*)&x ;  // Not a valid aliasing

// In C++
float *fp = reinterpret_cast<float*>(&x) ;  // Not a valid aliasing

printf( "%f\n", *fp ) ;

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

union u1
{
  int n;
  float f;
} ;

union u1 u;
u.f = 1.0f;

printf( "%d\n”, u.n );  // UB in C++ n is not the active member

यह C ++ में मान्य नहीं है और कुछ लोग यूनियनों के उद्देश्य को पूरी तरह से वैरिएंट प्रकारों को लागू करने के लिए मानते हैं और टाइप पाइंटिंग के लिए यूनियनों का उपयोग करने का अनुभव करते हैं।

हम कैसे सही तरीके से पुनरीक्षण करते हैं?

C और C ++ दोनों में टाइप पाइंटिंग के लिए मानक विधि यादगार है । यह एक छोटे से भारी हाथ लग सकता है लेकिन अनुकूलक के उपयोग की पहचान करनी चाहिए memcpy के लिए प्रकार punning और इसे दूर का अनुकूलन और चाल रजिस्टर करने के लिए एक रजिस्टर उत्पन्न करते हैं। उदाहरण के लिए यदि हम जानते हैं कि int64_t डबल के समान आकार है :

static_assert( sizeof( double ) == sizeof( int64_t ) );  // C++17 does not require a message

हम memcpy का उपयोग कर सकते हैं :

void func1( double d ) {
  std::int64_t n;
  std::memcpy(&n, &d, sizeof d); 
  //...

एक पर्याप्त अनुकूलन स्तर पर किसी भी सभ्य आधुनिक संकलक कि पहले उल्लेख के समान कोड उत्पन्न reinterpret_cast विधि या संघ के लिए विधि प्रकार punning । उत्पन्न कोड की जांच करने पर हम देखते हैं कि यह केवल रजिस्टर mov ( लाइव कंपाइलर एक्सप्लोरर उदाहरण ) का उपयोग करता है ।

C ++ 20 और bit_cast

C ++ 20 में हम बिट_कास्ट ( प्रस्ताव से लिंक में उपलब्ध कार्यान्वयन) प्राप्त कर सकते हैं, जो एक कॉन्स्ट्रेक्स संदर्भ में प्रयोग करने योग्य होने के साथ-साथ टाइप-वाक्य को एक सरल और सुरक्षित तरीका देता है।

निम्नलिखित कैसे उपयोग करने के लिए का एक उदाहरण है bit_cast एक यमक टाइप करने के लिए अहस्ताक्षरित पूर्णांक के लिए नाव , ( यह सीधा प्रसारण दिखाई ):

std::cout << bit_cast<float>(0x447a0000) << "\n" ; //assuming sizeof(float) == sizeof(unsigned int)

उस स्थिति में जहां To और From के समान आकार नहीं है, हमें एक मध्यवर्ती संरचना 15 का उपयोग करने की आवश्यकता होती है। हम एक struct एक युक्त का उपयोग करेगा sizeof (अहस्ताक्षरित int) चरित्र सरणी ( मान लिया गया 4 बाइट अहस्ताक्षरित int ) होने के लिए से प्रकार और अहस्ताक्षरित पूर्णांक के रूप में करने के लिए टाइप करें .:

struct uint_chars {
 unsigned char arr[sizeof( unsigned int )] = {} ;  // Assume sizeof( unsigned int ) == 4
};

// Assume len is a multiple of 4 
int bar( unsigned char *p, size_t len ) {
 int result = 0;

 for( size_t index = 0; index < len; index += sizeof(unsigned int) ) {
   uint_chars f;
   std::memcpy( f.arr, &p[index], sizeof(unsigned int));
   unsigned int result = bit_cast<unsigned int>(f);

   result += foo( result );
 }

 return result ;
}

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

कैचिंग स्ट्रिक्ट अलाइज़िंग वायलेशन

हमारे पास C ++ में सख्त अलियासिंग को पकड़ने के लिए बहुत सारे अच्छे उपकरण नहीं हैं, हमारे पास जो उपकरण हैं वे सख्त अलियासिंग उल्लंघन के कुछ मामलों और गलत लोड और स्टोर के कुछ मामलों को पकड़ लेंगे।

gcc का उपयोग करके फ्लैग -ऑफस्ट्रिक्ट-अलियासिंग और -स्ट्रिक्ट-अलियासिंग कुछ मामलों को पकड़ सकते हैं, हालांकि झूठी सकारात्मक / नकारात्मक के बिना नहीं। उदाहरण के लिए निम्नलिखित मामले gcc में एक चेतावनी उत्पन्न करेंगे ( इसे लाइव देखें ):

int a = 1;
short j;
float f = 1.f; // Originally not initialized but tis-kernel caught 
               // it was being accessed w/ an indeterminate value below

printf("%i\n", j = *(reinterpret_cast<short*>(&a)));
printf("%i\n", j = *(reinterpret_cast<int*>(&f)));

हालाँकि यह इस अतिरिक्त मामले को नहीं पकड़ेगा ( इसे लाइव देखें ):

int *p;

p=&a;
printf("%i\n", j = *(reinterpret_cast<short*>(p)));

हालांकि क्लैंग इन झंडों को अनुमति देता है लेकिन यह स्पष्ट रूप से चेतावनियों को लागू नहीं करता है।

एक और उपकरण जो हमारे पास उपलब्ध है वह है आसन जो गलत लोड और स्टोर को पकड़ सकता है। हालाँकि ये सीधे तौर पर सख्त अलिज़िंग उल्लंघन नहीं हैं लेकिन ये सख्त अलियासिंग उल्लंघन का एक सामान्य परिणाम हैं। उदाहरण के लिए, निम्न मामले- क्लेन्साइनेट = पते के उपयोग के साथ निर्मित होने पर रनटाइम त्रुटियाँ उत्पन्न करेंगे

int *x = new int[2];               // 8 bytes: [0,7].
int *u = (int*)((char*)x + 6);     // regardless of alignment of x this will not be an aligned address
*u = 1;                            // Access to range [6-9]
printf( "%d\n", *u );              // Access to range [6-9]

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

सी के लिए हमारे पास पहले से ही कवर किए गए सभी उपकरण हैं और हमारे पास टिस-इंटरप्रेटर भी है, एक स्थिर विश्लेषक जो सी भाषा के एक बड़े उपसमूह के लिए प्रोग्राम का विस्तृत विश्लेषण करता है। पहले के उदाहरण के C verions को देखते हुए जहां -fstrict-aliasing का एक मामला याद आता है ( इसे लाइव देखें )

int a = 1;
short j;
float f = 1.0 ;

printf("%i\n", j = *((short*)&a));
printf("%i\n", j = *((int*)&f));

int *p; 

p=&a;
printf("%i\n", j = *((short*)p));

tis-interpeter तीनों को पकड़ने में सक्षम है, निम्नलिखित उदाहरण tis-interpreter के रूप में tis-kernal को आमंत्रित करता है (आउटपुट संक्षिप्तता के लिए संपादित किया जाता है):

./bin/tis-kernel -sa example1.c 
...
example1.c:9:[sa] warning: The pointer (short *)(& a) has type short *. It violates strict aliasing
              rules by accessing a cell with effective type int.
...

example1.c:10:[sa] warning: The pointer (int *)(& f) has type int *. It violates strict aliasing rules by
              accessing a cell with effective type float.
              Callstack: main
...

example1.c:15:[sa] warning: The pointer (short *)p has type short *. It violates strict aliasing rules by
              accessing a cell with effective type int.

अंत में TySan है जो वर्तमान में विकास में है। यह सैनिटाइज़र एक छाया स्मृति खंड में टाइप चेकिंग जानकारी जोड़ता है और यह देखने के लिए एक्सेस की जांच करता है कि क्या वे अलियासिंग नियमों का उल्लंघन करते हैं। उपकरण संभावित रूप से सभी अलियासिंग उल्लंघनों को पकड़ने में सक्षम होना चाहिए, लेकिन एक बड़ा रन-टाइम ओवरहेड हो सकता है।


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
भार्गव राव

3
यदि मैं, +10, अच्छी तरह से लिखा और समझाया जा सकता है, दोनों पक्षों से भी, संकलक लेखकों और प्रोग्रामर ... केवल आलोचना: यह अच्छा होगा ऊपर के काउंटर उदाहरण हैं, यह देखने के लिए कि मानक द्वारा निषिद्ध क्या है, यह स्पष्ट नहीं है की तरह :-)
गेब्रियल

2
बहुत अच्छा जवाब। मुझे केवल इस बात का पछतावा है कि प्रारंभिक उदाहरण C ++ में दिए गए हैं, जिससे मेरे जैसे लोगों के लिए अनुसरण करना कठिन हो जाता है जो केवल C के बारे में जानते हैं या उनकी देखभाल करते हैं और यह नहीं जानते कि क्या कर reinterpret_castसकते हैं या क्या coutमतलब हो सकता है। (C ++ का उल्लेख करना सही है लेकिन मूल प्रश्न C और IIUC के बारे में था, ये उदाहरण वैसा ही हो सकता है जैसा कि सी। में लिखा गया है)
Gro-Tsen

टाइप करने की सजा के बारे में: इसलिए अगर मैं फ़ाइल में किसी प्रकार के एक्स का एक सरणी लिखता हूं, तो उस फाइल को मेमोरी में इंगित शून्य से पढ़ें *, तो मैंने उस सूचक को वास्तविक प्रकार के डेटा में डाल दिया ताकि उसका उपयोग किया जा सके - वह अपरिभाषित व्यवहार?
माइकल IV

44

सख्त अलियासिंग केवल संकेतकर्ताओं को संदर्भित नहीं करता है, यह संदर्भों को भी प्रभावित करता है, मैंने इसके बारे में एक पेपर को बढ़ावा देने वाले विकी के लिए लिखा था और यह इतनी अच्छी तरह से प्राप्त हुआ था कि मैंने इसे अपने परामर्श वेब साइट पर एक पृष्ठ में बदल दिया। यह पूरी तरह से समझाता है कि यह क्या है, यह लोगों को इतना भ्रमित क्यों करता है और इसके बारे में क्या करना है। सख्त अलियासिंग श्वेत पत्र । विशेष रूप से यह बताता है कि यूनियनें C ++ के लिए जोखिम भरा व्यवहार क्यों कर रही हैं, और मेमसीपी का उपयोग करना C और C ++ दोनों में एकमात्र फिक्स पोर्टेबल क्यों है। आशा है कि यह उपयोगी है।


3
" सख्त अलियासिंग केवल संकेतकर्ताओं को संदर्भित नहीं करता है, यह संदर्भों को भी प्रभावित करता है " वास्तव में, यह अंतरालों को संदर्भित करता है । " मेमेकपी का उपयोग करना एकमात्र फिक्स पोर्टेबल है " सुनो!
जिज्ञासु

5
अच्छा कागज। मेरा लेना: (1) यह अलियासिंग-'प्रोलेम 'खराब प्रोग्रामिंग के लिए एक अति-प्रतिक्रिया है - खराब प्रोग्रामर को उसकी बुरी आदतों से बचाने की कोशिश करना। यदि प्रोग्रामर में अच्छी आदतें हैं तो यह उपद्रव सिर्फ एक उपद्रव है और चेक को सुरक्षित रूप से बंद किया जा सकता है। (२) कम्पाइलर-साइड ऑप्टिमाइज़ेशन केवल अच्छी तरह से ज्ञात मामलों में किया जाना चाहिए और जब संदेह में स्रोत-कोड का सख्ती से पालन करना चाहिए; प्रोग्रामर को मजबूर करने के लिए कोड लिखने के लिए कंपाइलर की आइडियोसिंक्रेसिस को पूरा करने के लिए, बस, गलत है। इसे मानक का हिस्सा बनाने के लिए और भी बुरा।
स्लैशमाईस

4
@slashmais (1) " खराब प्रोग्रामिंग " नॉनसेंस के लिए एक अति-प्रतिक्रिया है । यह बुरी आदतों की अस्वीकृति है। तुम वो करते हो? आप मूल्य का भुगतान करते हैं: आपके लिए कोई गारंटी नहीं! (२) प्रसिद्ध मामले? कौन सा? सख्त अलियासिंग नियम "अच्छी तरह से ज्ञात" होना चाहिए!
१५:४५ पर उत्सुकताजनक

5
@ क्यूरियस गुय: भ्रम के कुछ बिंदुओं को साफ करने के बाद, यह स्पष्ट है कि अलियासिंग नियमों के साथ सी भाषा कार्यक्रमों के लिए प्रकार-अज्ञेय स्मृति पूल को लागू करना असंभव बनाती है। कुछ प्रकार के कार्यक्रम मॉलॉक / मुफ्त के साथ मिल सकते हैं, लेकिन दूसरों को हाथ में काम करने के लिए बेहतर स्मृति प्रबंधन तर्क की आवश्यकता होती है। मुझे आश्चर्य है कि क्यों C89 औचित्य ने अलियासिंग नियम के कारण के इस तरह के एक गंभीर उदाहरण का उपयोग किया, क्योंकि उनके उदाहरण से ऐसा लगता है कि नियम किसी भी उचित कार्य को करने में कोई बड़ी कठिनाई पैदा नहीं करेगा।
सुपरकाट

5
@curiousguy, अधिकांश संकलक बाहर का मुकदमा करते हैं, जिसमें -O3 के रूप में -fstrict-aliasing शामिल हैं और यह छिपा हुआ अनुबंध उन उपयोगकर्ताओं पर मजबूर किया गया है जिन्होंने TBAA के बारे में कभी नहीं सुना है और कोड लिखा है जैसे कि सिस्टम प्रोग्राम कैसे हो सकता है। मेरा मतलब सिस्टम प्रोग्रामर के प्रति असंगत लगने का नहीं है, लेकिन इस तरह के अनुकूलन को डिफ़ॉल्ट ऑप्ट-ओ 3 के बाहर छोड़ दिया जाना चाहिए और उन लोगों के लिए ऑप्ट-ऑप्ट ऑप्टिमाइज़ेशन होना चाहिए जो जानते हैं कि टीबीए क्या है। यह संकलक 'बग' को देखकर मज़ेदार नहीं है, जो कि TBAA का उल्लंघन करने वाला उपयोगकर्ता कोड है, विशेष रूप से उपयोगकर्ता कोड में स्रोत स्तर के उल्लंघन को ट्रैक करता है।
१६:१६ की रात १४:१६

34

डग टी के पहले से ही लिखे गए परिशिष्ट के रूप में, यहाँ एक सरल परीक्षण मामला है जो शायद इसे gcc से चलाता है:

check.c

#include <stdio.h>

void check(short *h,long *k)
{
    *h=5;
    *k=6;
    if (*h == 5)
        printf("strict aliasing problem\n");
}

int main(void)
{
    long      k[1];
    check((short *)k,k);
    return 0;
}

के साथ संकलित करें gcc -O2 -o check check.c। आमतौर पर (अधिकांश gcc संस्करणों के साथ मैंने कोशिश की) यह "सख्त अलियासिंग समस्या" को उत्पन्न करता है, क्योंकि संकलक मानता है कि "h" "चेक" फ़ंक्शन में "k" के समान पता नहीं हो सकता है। इसके कारण कंपाइलर if (*h == 5)दूर का अनुकूलन करता है और हमेशा प्रिंटफ को कॉल करता है।

जो लोग यहाँ रुचि रखते हैं उनके लिए x64 कोडांतरण कोड है, जो कि gcc 4.6.3 द्वारा निर्मित है, ubuntu 12.04.2 पर चल रहा है:

movw    $5, (%rdi)
movq    $6, (%rsi)
movl    $.LC0, %edi
jmp puts

तो अगर हालत पूरी तरह से कोडांतरक कोड से चला गया है।


अगर आप जाँच करने के लिए एक दूसरा छोटा * j जोड़ते हैं () और इसका इस्तेमाल करते हैं (* j = 7) तो ऑप्टिमाइज़ेशन डिसैपियर होता है क्योंकि ggc ऐसा नहीं करता है यदि h और j समान मान के लिए वास्तविक बिंदु नहीं हैं। हाँ अनुकूलन वास्तव में स्मार्ट है।
दलीप लार्डी

2
चीजों को अधिक मज़ेदार बनाने के लिए, उन पॉइंटर्स का उपयोग करें, जो संगत नहीं हैं, लेकिन उनका आकार और प्रतिनिधित्व समान है (कुछ प्रणालियों पर जो कि सत्य है जैसे * long long*और int64_t*)। एक उम्मीद कर सकते हैं कि एक समझदार संकलक पहचान करनी चाहिए कि एक long long*और int64_t*एक ही भंडारण अगर वे हूबहू संग्रह किए गए हैं यहां पहुंच सकता है, लेकिन इस तरह के उपचार फैशनेबल नहीं रह गया है।
सुपरकैट

Grr ... x64 एक Microsoft सम्मेलन है। इसके बजाय amd64 या x86_64 का उपयोग करें।
एसएस ऐनी

Grr ... x64 एक Microsoft सम्मेलन है। इसके बजाय amd64 या x86_64 का उपयोग करें।
एसएस ऐनी

17

पॉइंटर कास्ट्स (जैसा कि एक यूनियन का उपयोग करने का विरोध किया गया है) के माध्यम से टाइप करें, सख्त अलियासिंग को तोड़ने का एक प्रमुख उदाहरण है।


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

@ शफीकयागमोर: C89 ने स्पष्ट रूप से कार्यान्वयनकर्ताओं को उन मामलों का चयन करने की अनुमति दी, जिनमें वे यूनियनों के माध्यम से टाइप पाइंटिंग को उपयोगी नहीं मानते हैं या नहीं करेंगे। एक कार्यान्वयन, उदाहरण के लिए, यह निर्दिष्ट कर सकता है कि लिखने के लिए एक प्रकार के बाद दूसरे को पढ़ने के लिए टाइप पेन्मेंट के रूप में पहचाना जाए, अगर प्रोग्रामर ने लिखित और रीड के बीच निम्नलिखित में से कोई एक किया हो: (1) जिसमें एक लैवल का मूल्यांकन किया गया हो संघ का प्रकार [सदस्य का पता लेना योग्य होगा, यदि अनुक्रम में सही बिंदु पर किया गया हो]; (२) एक पॉइंटर को एक पॉइंटर से दूसरे में पॉइंटर में बदलना, और उस पीटीआर के माध्यम से एक्सेस करना।
सुपरकैट

@ शफीक्यगहमौर: एक कार्यान्वयन यह भी निर्दिष्ट कर सकता है कि पूर्णांक और फ्लोटिंग-पॉइंट मानों के बीच टाइपिंग पैंतरेबाज़ी केवल तभी मज़बूती से काम करेगी जब कोड fpsync()fp के रूप में लिखने और int या इसके विपरीत पढ़ने के बीच एक निर्देश निष्पादित करता है [अलग पूर्णांक और FPU पाइपलाइनों और कैश के साथ कार्यान्वयन] , ऐसा निर्देश महंगा हो सकता है, लेकिन उतना महंगा नहीं जितना कि कंपाइलर हर यूनियन एक्सेस पर इस तरह का सिंक्रोनाइज़ेशन करता हो]। या एक कार्यान्वयन यह निर्दिष्ट कर सकता है कि परिणामी मूल्य सामान्य प्रारंभिक अनुक्रमों का उपयोग करने वाली परिस्थितियों को छोड़कर कभी भी उपयोग करने योग्य नहीं होगा।
सुपरकाट

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

17

C89 के तर्क के अनुसार, मानक के लेखकों को इस तरह के संकलक की आवश्यकता नहीं होनी चाहिए:

int x;
int test(double *p)
{
  x=5;
  *p = 1.0;
  return x;
}

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

दुर्भाग्य से, C89 के लेखकों ने अपना नियम इस तरह से लिखा है कि, अगर सचमुच पढ़ा जाए, तो निम्न कार्य भी अपरिभाषित व्यवहार को आमंत्रित करेगा:

void test(void)
{
  struct S {int x;} s;
  s.x = 1;
}

क्योंकि यह प्रकार की intवस्तु का उपयोग करने के लिए प्रकार का एक अंतराल का उपयोग करता है struct S, और intउन प्रकारों में से नहीं है , जिनका उपयोग किया जा सकता है a struct S। क्योंकि यह अपरिभाषित व्यवहार के रूप में गैर-वर्ण-प्रकार के सदस्यों और संघों के सभी प्रकार के उपयोग के लिए बेतुका होगा, लगभग सभी लोग मानते हैं कि कम से कम कुछ परिस्थितियां हैं जहां एक प्रकार का एक अंतराल का उपयोग किसी अन्य प्रकार की वस्तु तक पहुंचने के लिए किया जा सकता है। । दुर्भाग्य से, सी मानक समिति उन परिस्थितियों को परिभाषित करने में विफल रही है।

अधिकांश समस्या दोषपूर्ण रिपोर्ट # 028 का परिणाम है, जिसने एक कार्यक्रम के व्यवहार के बारे में पूछा:

int test(int *ip, double *dp)
{
  *ip = 1;
  *dp = 1.23;
  return *ip;
}
int test2(void)
{
  union U { int i; double d; } u;
  return test(&u.i, &u.d);
}

दोष रिपोर्ट # 28 में कहा गया है कि कार्यक्रम अपरिभाषित व्यवहार को आमंत्रित करता है क्योंकि टाइप "डबल" के एक यूनियन सदस्य को लिखने और "इंट" में से एक को पढ़ने की कार्रवाई कार्यान्वयन-परिभाषित व्यवहार को आमंत्रित करती है। ऐसा तर्क निरर्थक है, लेकिन प्रभावी प्रकार के नियमों के लिए आधार बनाता है जो मूल समस्या को दूर करने के लिए कुछ भी नहीं करते हुए भाषा को अनावश्यक रूप से जटिल करते हैं।

मूल समस्या को हल करने का सबसे अच्छा तरीका संभवतः नियम के उद्देश्य के बारे में फ़ुटनोट का इलाज करना होगा जैसे कि यह नियमात्मक थे, और नियम को उन मामलों को छोड़कर अप्राप्य बना दिया, जिनमें वास्तव में एलियंस का उपयोग करके परस्पर विरोधी पहुंच शामिल है। कुछ दिया जैसे:

 void inc_int(int *p) { *p = 3; }
 int test(void)
 {
   int *p;
   struct S { int x; } s;
   s.x = 1;
   p = &s.x;
   inc_int(p);
   return s.x;
 }

भीतर कोई विरोध नहीं है inc_intक्योंकि स्टोर किए गए स्टोरेज तक सभी पहुंच *pप्रकार के अंतराल के साथ की जाती है int, और इसमें कोई संघर्ष नहीं है testक्योंकि pयह एक से प्राप्त होता है struct S, और अगली बार जब sतक उपयोग किया जाता है, तब उस स्टोरेज तक सभी पहुंच जाते हैं। के माध्यम pसे पहले ही हो चुका होगा।

यदि कोड थोड़ा बदल दिया गया था ...

 void inc_int(int *p) { *p = 3; }
 int test(void)
 {
   int *p;
   struct S { int x; } s;
   p = &s.x;
   s.x = 1;  //  !!*!!
   *p += 1;
   return s.x;
 }

यहां, चिह्नित लाइन पर pपहुंच के बीच एक अलियासिंग संघर्ष है और s.xनिष्पादन में उस बिंदु पर एक और संदर्भ मौजूद है जिसका उपयोग उसी भंडारण तक पहुंचने के लिए किया जाएगा

डिफेक्ट रिपोर्ट 028 में दो बिंदुओं के निर्माण और उपयोग के बीच ओवरलैप के कारण मूल उदाहरण यूबी को आमंत्रित किया गया था, जिसने "प्रभावी प्रकार" या ऐसी अन्य जटिलता को जोड़ने के बिना चीजों को बहुत अधिक स्पष्ट कर दिया होगा।


ठीक है, यह दिलचस्प होगा कि किस प्रकार का कमोबेश "मानक समिति क्या कर सकती है" के एक प्रस्ताव को पढ़ना दिलचस्प होगा जिसने बिना किसी जटिलता के परिचय दिए अपने लक्ष्यों को प्राप्त किया।
जूनियर

1
@jrh: मुझे लगता है कि यह बहुत आसान होगा। यह पहचानें कि 1. किसी फ़ंक्शन या लूप के किसी विशेष निष्पादन के दौरान होने वाले उपनाम के लिए, उस निष्पादन के दौरान दो अलग-अलग पॉइंटर्स या लैवल का उपयोग किया जाना चाहिए ताकि परस्पर विरोधी फैशन में एक ही भंडारण को संबोधित किया जा सके ; 2. पहचानें कि संदर्भों में जहां एक सूचक या अंतराल ताजा रूप से दूसरे से प्राप्त होता है, दूसरे तक पहुंच पहले की पहुंच है; 3. मान्यता है कि नियम उन मामलों में लागू करने का इरादा नहीं है जो वास्तव में अलियासिंग शामिल नहीं करते हैं।
सुपरकैट जूल

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

11

कई उत्तरों को पढ़ने के बाद, मुझे कुछ जोड़ने की आवश्यकता महसूस हुई:

सख्त अलियासिंग (जो मैं थोड़ा वर्णन करूंगा) महत्वपूर्ण है क्योंकि :

  1. मेमोरी एक्सेस महंगी (प्रदर्शन वार) हो सकती है, यही कारण है कि भौतिक मेमोरी में वापस लिखे जाने से पहले सीपीयू रजिस्टरों में डेटा में हेरफेर किया जाता है

  2. यदि दो अलग-अलग सीपीयू रजिस्टरों में डेटा एक ही मेमोरी स्पेस पर लिखा जाएगा, तो हम यह अनुमान नहीं लगा सकते कि सी में कोड होने पर कौन सा डेटा "जीवित" रहेगा

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

चूंकि दो पॉइंटर्स मेमोरी में एक ही स्थान को इंगित कर सकते हैं, इसलिए यह जटिल कोड हो सकता है जो संभव टकराव को संभालता है

यह अतिरिक्त कोड धीमा है और प्रदर्शन को नुकसान पहुंचाता है क्योंकि यह अतिरिक्त मेमोरी रीड / राइट ऑपरेशन करता है जो धीमी और (संभवतः) दोनों अनावश्यक हैं।

सख्त अलियासिंग नियम हमें अनावश्यक मशीन कोड से बचने के लिए अनुमति देता है जिन मामलों में उस में होना चाहिए ग्रहण करने के लिए है कि दो संकेत दिए गए एक ही स्मृति ब्लॉक को इंगित नहीं है सुरक्षित (यह भी देखें restrictकीवर्ड)।

स्ट्रिक्ट अलियासिंग में कहा गया है कि यह मान लेना सुरक्षित है कि विभिन्न प्रकार के पॉइंटर्स मेमोरी में अलग-अलग स्थानों पर इंगित करते हैं।

यदि कोई कंपाइलर यह नोटिस करता है कि दो पॉइंटर्स विभिन्न प्रकारों (उदाहरण के लिए, int *a float *) की ओर इशारा करते हैं , तो यह मान लेगा कि मेमोरी एड्रेस अलग है और यह मेमोरी एड्रेस की टक्करों से बचाव नहीं करेगा , जिसके परिणामस्वरूप तेज मशीन कोड होगा।

उदाहरण के लिए :

निम्नलिखित फ़ंक्शन को मानें:

void merge_two_ints(int *a, int *b) {
  *b += *a;
  *a += *b;
}

उस मामले को संभालने के लिए जिसमें a == b(दोनों पॉइंटर्स एक ही मेमोरी को इंगित करते हैं), हमें सीपीयू रजिस्टरों में मेमोरी से डेटा लोड करने के तरीके को ऑर्डर करने और परीक्षण करने की आवश्यकता होती है, इसलिए कोड इस तरह समाप्त हो सकता है:

  1. लोड aऔर bमेमोरी से।

  2. जोड़ने aके लिए b

  3. सहेजें b और पुनः लोड करें a

    (सीपीयू रजिस्टर से मेमोरी में सेव करें और मेमोरी से सीपीयू रजिस्टर में लोड करें)।

  4. जोड़ने bके लिए a

  5. a(CPU रजिस्टर से) मेमोरी में सेव करें ।

चरण 3 बहुत धीमा है क्योंकि इसे भौतिक मेमोरी तक पहुंचने की आवश्यकता है। हालांकि, यह मामलों में जहां के खिलाफ की रक्षा करने के लिए आवश्यक है aऔर bएक ही स्मृति का पता करने के लिए बिंदु।

सख्त उर्फिंग हमें संकलक को यह बताकर इसे रोकने की अनुमति देगा कि ये मेमोरी पते अलग-अलग हैं (जो, इस मामले में, आगे भी अनुकूलन की अनुमति देगा जो कि संकेत मेमोरी साझा करने पर नहीं किया जा सकता है)।

  1. यह दो प्रकार से संकलक को बताया जा सकता है, विभिन्न प्रकारों का उपयोग करके इंगित करने के लिए। अर्थात:

    void merge_two_numbers(int *a, long *b) {...}
  2. restrictकीवर्ड का उपयोग करना । अर्थात:

    void merge_two_ints(int * restrict a, int * restrict b) {...}

अब, सख्त एलियासिंग नियम को संतुष्ट करने से, चरण 3 से बचा जा सकता है और कोड काफी तेजी से चलेगा।

वास्तव में, restrictकीवर्ड को जोड़कर , पूरे फ़ंक्शन को इसके लिए अनुकूलित किया जा सकता है:

  1. लोड aऔर bमेमोरी से।

  2. जोड़ने aके लिए b

  3. बचाने के लिए दोनों aको b

यह अनुकूलन संभव टक्कर की वजह से पहले से नहीं किया जा सकता था किया गया है, (जहां aऔर bके बजाय तीन गुना किया जाएगा दोगुनी)।


प्रतिबंधित कीवर्ड के साथ, चरण 3 पर, क्या केवल 'b' के परिणाम नहीं सहेजे जाने चाहिए? ऐसा लगता है जैसे योग का परिणाम 'ए' में भी संग्रहीत किया जाएगा। क्या इसे फिर से लोड किए जाने की आवश्यकता है?
नील

1
@NeilB - हां तुम सही हो। हम केवल बचत कर रहे हैं b(इसे पुनः लोड नहीं कर रहे हैं ) और पुनः लोड कर रहे हैं a। मुझे उम्मीद है कि अब यह स्पष्ट हो जाएगा।
मिस्ट

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

ध्यान दें कि हालांकि मुख्य रैम से लोड करना बहुत धीमा है (और यदि लंबे समय तक सीपीयू कोर को स्टाल किया जा सकता है, तो निम्नलिखित ऑपरेशन परिणाम पर निर्भर करते हैं), L1 कैश से लोड करना बहुत तेज़ है, और इसलिए हाल ही में लिख रहे कैश लाइन पर लिख रहा है उसी कोर द्वारा इसलिए सभी लेकिन पहली बार पढ़ने या किसी पते पर लिखने के लिए आमतौर पर बहुत तेजी से होगा: रेग / मेम एड्र एक्सेस के बीच का अंतर कैशेड / अनएच्ड मेम एडर के बीच के अंतर से छोटा होता है।
जिज्ञासु

@curiousguy - हालाँकि आप सही हैं, लेकिन इस मामले में "तेज़" सापेक्ष है। L1 कैश संभवतः अभी भी CPU रजिस्टरों की तुलना में परिमाण धीमा करने का एक क्रम है (मुझे लगता है कि 10 गुना से अधिक धीमा)। इसके अलावा, restrictकीवर्ड न केवल संचालन की गति को कम करता है, बल्कि उनकी संख्या भी है, जो सार्थक हो सकती है ... मेरा मतलब है, आखिरकार, सबसे तेज ऑपरेशन कोई भी ऑपरेशन नहीं है :)
रहस्य

6

सख्त अलियासिंग अलग-अलग पॉइंटर प्रकारों को एक ही डेटा की अनुमति नहीं दे रहा है।

इस लेख से आपको इस मुद्दे को पूरी तरह से समझने में मदद मिलेगी।


4
आप संदर्भों के बीच और एक संदर्भ और एक सूचक के बीच भी उपनाम कर सकते हैं। मेरा ट्यूटोरियल देखें dbp-consulting.com/tutorials/StrictAliasing.html
phagon1

4
इसे एक ही डेटा के लिए अलग-अलग पॉइंटर प्रकार रखने की अनुमति है। जहां सख्त अलियासिंग आती है, जब वही मेमोरी लोकेशन एक पॉइंटर टाइप के माध्यम से लिखी जाती है और दूसरे के माध्यम से पढ़ी जाती है। इसके अलावा, कुछ अलग प्रकारों की अनुमति है (जैसे intऔर एक संरचना जिसमें शामिल है int)।
MM

-3

तकनीकी रूप से C ++ में, सख्त अलियासिंग नियम शायद कभी लागू नहीं होता है।

अप्रत्यक्ष की परिभाषा पर ध्यान दें ( * ऑपरेटर ):

यूनीरी * ऑपरेटर अप्रत्यक्ष प्रदर्शन करता है: जिस अभिव्यक्ति के लिए इसे लागू किया जाता है वह एक ऑब्जेक्ट प्रकार के लिए एक संकेतक होगा, या एक फ़ंक्शन प्रकार के लिए एक संकेतक होगा और परिणाम वस्तु या फ़ंक्शन का एक अंतराल है जो अभिव्यक्ति को इंगित करता है

इसके अलावा glvalue की परिभाषा से

एक चमक एक अभिव्यक्ति है जिसका मूल्यांकन किसी वस्तु की पहचान को निर्धारित करता है, (... स्निप)

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


4
C मानक कई विभिन्न अवधारणाओं को संदर्भित करने के लिए "ऑब्जेक्ट" शब्द का उपयोग करता है। उनमें से, बाइट्स का एक क्रम जो किसी उद्देश्य के लिए विशेष रूप से आवंटित किया जाता है, बाइट्स के अनुक्रम के लिए एक जरूरी-अनन्य-विशेष संदर्भ / जिससे एक विशेष प्रकार का मूल्य लिखा या पढ़ा जा सकता है, या ऐसा संदर्भ जो वास्तव में है किसी संदर्भ में एक्सेस किया जाएगा या किया जाएगा। मुझे नहीं लगता कि "ऑब्जेक्ट" शब्द को परिभाषित करने का कोई समझदार तरीका है जो मानक द्वारा उपयोग किए जाने वाले सभी तरीकों के अनुरूप होगा।
सुपरकैट

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

1
@FrankHB: यदि कोई घोषित करता है int foo;, तो क्या लवलीन अभिव्यक्ति द्वारा पहुँचा जाता है *(char*)&foo? क्या वह प्रकार की वस्तु है char? क्या वह वस्तु उसी समय अस्तित्व में आती है foo? fooटाइप की उक्त वस्तु के संग्रहीत मूल्य को बदलने के लिए लिखना चाहेंगे char? यदि ऐसा है, तो क्या ऐसा कोई नियम है जो किसी प्रकार के ऑब्जेक्ट का संग्रहित मूल्य टाइप के charएक अंतराल का उपयोग करके एक्सेस करने की अनुमति देगा int?
सुपरकैट

@FrankHB: 6.5p7 की अनुपस्थिति में, कोई बस यह कह सकता है कि भंडारण के हर क्षेत्र में एक साथ सभी प्रकार की सभी वस्तुएं होती हैं जो भंडारण के उस क्षेत्र में फिट हो सकती हैं, और भंडारण के उस क्षेत्र तक पहुंचने के साथ-साथ उन सभी तक पहुंच होती है। इस तरह की शैली में 6.5p7 में "ऑब्जेक्ट" शब्द का उपयोग करना, हालांकि, गैर-चरित्र-प्रकार के अंतराल के साथ कुछ भी करने से मना करना होगा, जो स्पष्ट रूप से एक बेतुका परिणाम होगा और शासन के उद्देश्य को पूरी तरह से हरा देगा। इसके अलावा, 6.5p6 के अलावा हर जगह इस्तेमाल की जाने वाली "ऑब्जेक्ट" की अवधारणा में एक स्थिर संकलन-समय प्रकार है, लेकिन ...
सुपरटैट

1
sizeof (int) 4 है, घोषणा int i;प्रत्येक चरित्र के चार ऑब्जेक्ट्स in addition to one of type int ? I see no way to apply a consistent definition of "object" which would allow for operations on both * (char *) & i` और बनाती है i। अंत में, मानक में ऐसा कुछ भी नहीं है जो volatileहार्डवेयर रजिस्टरों तक पहुँचने के लिए एक- अयोग्य सूचक को भी अनुमति देता है जो "ऑब्जेक्ट" की परिभाषा को पूरा नहीं करता है।
सुपरैट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.