2017-09-08 13 views
15

एक उदाहरण के रूप निम्नलिखित स्निपेट पर विचार करें:एक char * dereferencing सख्त एलियासिंग अनुकूलन अवरोध करता है?

*pInt = 0xFFFF; 
*pFloat = 5.0; 

क्योंकि वे int और float संकेत दिए गए हैं, संकलक वे अन्य नाम नहीं करते हैं और उदाहरण के लिए उन्हें विनिमय कर सकते हैं ग्रहण करेगा।

अब मान लेते हैं कि हम इसे को मसाले इस के साथ करते हैं:

*pInt = 0xFFFF; 
*pChar = 'X'; 
*pFloat = 5.0; 

char* चूंकि यह *pInt लेकर जा सकते हैं कुछ भी अन्य नाम पर अनुमति दी है, है, तो *pInt असाइनमेंट *pChar का काम परे नहीं ले जाया जा सकता है, क्योंकि यह वैध रूप से *pInt पर इंगित कर सकता है और अपना पहला बाइट 'एक्स' पर सेट कर सकता है। इसी प्रकार pChar*pFloat पर इंगित कर सकता है, *pFloat को असाइनमेंट को चार असाइनमेंट से पहले स्थानांतरित नहीं किया जा सकता है, क्योंकि कोड *pFloat को पुन: असाइन करके पिछली बाइट सेटिंग के प्रभाव को कम करने का इरादा रख सकता है।

क्या इसका मतलब है कि मैं पुनर्नवीनीकरण और अन्य सख्त एलियासिंग संबंधित अनुकूलन के लिए बाधाओं को बनाने के लिए char* के माध्यम से लिख और पढ़ सकता हूं?

+4

यह [उदाहरण] (https://godbolt.org/g/k2yEzP) दिखाता है कि आपका अंतर्ज्ञान सही है (कम से कम उस कार्यान्वयन के लिए)। यदि आप असेंबली में उपयोग नहीं किए जाते हैं तो यह स्पष्ट नहीं हो सकता है कि पहले फ़ंक्शन के लिए '* ए = 1' को नीचे ले जाया गया है और' * a = * a + 1' के साथ विलय हो गया है। दूसरे मामले में, यह रोका गया है। –

उत्तर

3

मुझे लगता है कि सामान्य रूप से आप इसे अनुक्रमित बाधा के रूप में उपयोग नहीं कर सकते हैं। कारण यह है कि संकलक सरल मामले तो आप इस सब पर कोई फायदे हैं प्रस्तुत कर रहे हैं कि के लिए

if (pInt == pChar || pFloat == pChar) { 
    // be careful 
} else { 
    // no aliasing 
} 

जाहिर है अपने कोड की versionning किसी प्रकार का कर सकता है,, लेकिन फायदेमंद साबित हो सकता आपके संकेत नहीं बदलता है तो है कोड के एक बड़े खंड में।

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

एकमात्र ऐसा डेटा जो अन्यथा असंबंधित है जिसके लिए सी मानक रीडरिंग गारंटी देता है परमाणु वस्तुएं होती हैं जो अनुक्रमिक स्थिरता के साथ संचालित होती हैं।

+1

'pChar' को पॉइंटर्स के लिए उपनाम के लिए' pInt' के समान पते की आवश्यकता नहीं है। 'pChar'' pInt' द्वारा दिए गए पते से नीचे एक या कई बाइट्स को नीचे इंगित कर सकता है। दोनों पॉइंटर्स डेटा के समान 32 बिट हिस्से को किसी भी तरह से बदल देंगे, फिर भी अलग-अलग पते हैं। – Lundin

+0

@ लंदन, हां, जो 'if' थोड़ा अधिक जटिल में चेक करेगा, लेकिन मुझे नहीं लगता कि यह मुख्य तर्क को बदलता है। पॉइंटर्स की तुलना करना जो एक ही ऑब्जेक्ट को इंगित नहीं करते हैं, अनिर्धारित है, इसलिए सी * कोड * जैसा कि यहां लिखा गया है, यह संभव नहीं है। लेकिन संकलक चीजों को अपरिभाषित किए बिना कुछ समकक्ष कर सकता है। –

+0

@JensGustedt: स्थिति जो आप वर्णन करते हैं उससे भी बदतर है। जीसीसी और क्लैंग दोनों चरित्र-प्रकार की पहुंच को बाहर निकाल देंगे जो बाइट्स को उनके द्वारा पहले से रखे गए मूल्यों के साथ लिखते हैं, भले ही इस तरह के एक्सेस को प्रश्न में भंडारण के प्रभावी प्रकार को मिटाने के लिए जरूरी हो, और कार्यक्रम का व्यवहार मानक द्वारा परिभाषित किया गया था जब ऐसी पहुंच मौजूद थी। – supercat

5

पॉइंटर एलियासिंग ज्यादातर परिदृश्यों में समझ में आता है जब संकलक यह नहीं जानता कि कोई पॉइंटर परिवर्तक अन्य पॉइंटर या नहीं है। इस मामले में जब आप कॉलर की तुलना में एक अलग अनुवाद इकाई में स्थित फ़ंक्शन संकलित करते हैं।

void func (char* pChar, float* pFloat) 
{ 
    *pChar = 'X'; 
    *pFloat = 5.0; 
} 

यहाँ pFloat काम वास्तव में, pChar एक से पहले अनुक्रम नहीं जा सकता क्योंकि संकलक घटा नहीं कर सकता कि pCharpFloat रूप में एक ही स्थान पर इंगित नहीं करता है।

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

मतलब यह है कि पॉइंटर्स वास्तव में ओवरलैपिंग मेमोरी पर पॉइंटर्स वास्तव में उपनाम/बिंदु करते हैं तो आपको केवल स्मृति अवरोध-जैसे व्यवहार मिलेगा। यदि नहीं, तो निर्देश आदेश के संबंध में सभी दांव बंद हो जाएंगे। तो शायद यह एक तंत्र नहीं है जिस पर आपको भरोसा करना चाहिए।

+0

आपको सही यह केवल तब होता है जब आप मानते हैं कि पॉइंटर्स प्रतिबंधित नहीं हैं और साइड इफेक्ट प्रवण हैं। https://godbolt.org/g/EVJr8G –

0

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

struct s1 {unsigned short x;}; 
struct s2 {unsigned short x;}; 

int test(struct s1 *p1, struct s2 *p2) 
{ 
    if (p1->x) 
    { 
    p2->x = 12; 
    unsigned char *cp = (unsigned char*)p1; 
    unsigned char c0=cp[0]^1,c1=cp[1]^2; 
    cp[0]=c0^1; cp[1]=c1^2; 
    } 
    return p1->x; 
} 

दोनों बजना और जीसीसी कोड है कि मूल्य कि p1 था जब "अगर" बयान मार डाला गया था रिटर्न उत्पन्न होगा। मुझे मानक में कुछ भी नहीं दिखता है जो उचित होगा (यदि पी 1 == पी 2, * पी 2 की पूरी सामग्री चरित्र प्रकारों के माध्यम से "char" प्रकार की अलग-अलग वस्तुओं में पढ़ी जाएगी, जो परिभाषित व्यवहार है, और उन अलग वस्तुओं की सामग्री * पी 1 की संपूर्ण सामग्री को ओवरराइट करने के लिए उपयोग किया जाएगा, जो परिभाषित व्यवहार भी है) लेकिन जीसीसी और क्लैंग दोनों निर्णय लेंगे कि चूंकि सीपी [0] और सीपी [1] में लिखे गए मान पहले से मौजूद हैं, उन परिचालनों को छोड़ दें।

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