अन्य चीजों के अलावा, सी99 ने एक प्रोग्रामर के लिए यह निर्दिष्ट करने के तरीके के रूप में प्रतिबंधित कीवर्ड जोड़ा कि एक सूचक एक दायरे में दिए गए ऑब्जेक्ट के लिए केवल सूचक है और, परिणामस्वरूप, कंपाइलर को एक "संकेत" देता है "यह उस पॉइंटर के माध्यम से ऑब्जेक्ट तक पहुंचने पर अतिरिक्त अनुकूलन कर सकता है।
उस समस्या को स्पष्ट करने के लिए जिसे प्रतिबंधित करना था, एक फ़ंक्शन पर विचार करें:
void update_ptrs( int *p, int *q, int const *v ) { *p = *v; *q = *v; }
जिसके लिए कंपाइलर x86-64 कोड जेनरेट करेगा जैसे:
mov eax, [rdx] ; tmp = *v // 1 add [rdi], eax ; *p = tmp mov eax, [rdx] ; tmp = *v // 3 add [rsi], eax ; *q = tmp
आपको आश्चर्य हो सकता है कि यह पंक्ति 3 क्यों उत्पन्न करता है क्योंकि यह पंक्ति 1 के साथ अनावश्यक लगती है। समस्या यह है कि संकलक यह नहीं जान सकता कि आपने ऐसा कुछ नहीं किया है:
int x = 1, v = 2; update_ptrs( &v, &x, &v ); // x = 5, v = 4
update_ptrs() में, p और v alias same int होगा, इसलिए कंपाइलर को इसे सुरक्षित रखना होगा और मान लेना होगा कि *v का मान रीड्स के बीच बदल सकता है, इसलिए अतिरिक्त mov निर्देश।
सामान्य तौर पर, सी में पॉइंटर्स ऑप्टिमाइज़ेशन को भ्रमित करते हैं क्योंकि कंपाइलर यह नहीं जान सकता है कि दो पॉइंटर्स एक-दूसरे के उपनाम हैं या नहीं। प्रदर्शन महत्वपूर्ण कोड में, एलिडिंग मेमोरी में लिखा है हो सकता है एक बड़ी जीत हो अगर कंपाइलर इसे सुरक्षित रूप से कर सकता है।
उपर्युक्त समस्या को हल करने के लिए, सी में प्रतिबंधित जोड़ा गया था ताकि आप यह निर्दिष्ट कर सकें कि दिया गया पॉइंटर पॉइंटर के दायरे में किसी ऑब्जेक्ट के लिए केवल पॉइंटर है, यानी, समान स्कोप उपनाम में कोई अन्य पॉइंटर नहीं है यह।
प्रतिबंध का उपयोग करने के लिए, आप इसेके बीच * और एक घोषणा में सूचक का नाम डालते हैं। प्रतिबंधित का उपयोग करने के लिए पुनः लिखा गया update_ptrs() होगा:
void update_ptrs_v2( int *restrict p, int *restrict q, int const *restrict v ) { *p = *v; *q = *v; }(दाएं से बाएं पढ़ें, उदाहरण के लिए, v एक स्थिरांक int के लिए एक प्रतिबंधित सूचक है; या cdecl का उपयोग करें।)
प्रतिबंध जोड़कर, कंपाइलर अब इस तरह कोड उत्पन्न कर सकता है:
void update_ptrs_v2( int *restrict p, int *restrict q, int const *restrict v ) { *p = *v; *q = *v; }अब, कंपाइलर अतिरिक्त mov निर्देश की पिछली पंक्ति 3 को हटाने में सक्षम था।
शायद सबसे प्रसिद्ध उदाहरण जहां प्रतिबंधित का उपयोग किया जाता है वह मानक लाइब्रेरी फ़ंक्शन memcpy() है। यह मेमोरी के एक हिस्से को कॉपी करने का सबसे तेज़ तरीका है
यदि स्रोत और गंतव्य पते नहीं ओवरलैप होते हैं। जब पते do ओवरलैप होते हैं तो थोड़ा धीमा memmove() फ़ंक्शन उपयोग के लिए मौजूद होता है।
नुकसानdo एक दूसरे को update_ptrs_v2() या memcpy() कहते हैं। कुछ मामलों में, कंपाइलर आपको चेतावनी दे सकता है, लेकिन सभी मामलों में नहीं, इसलिए दुरुपयोग पकड़ने के लिए कंपाइलर पर निर्भर न रहें।
ध्यान दें कि प्रतिबंध एक दिए गए दायरे के लिए है। एक प्रतिबंधित सूचक को दूसरेसमान दायरे में निर्दिष्ट करने से के परिणामस्वरूप अपरिभाषित व्यवहार होता है:
void update_ptrs_v2( int *restrict p, int *restrict q, int const *restrict v ) { *p = *v; *q = *v; }हालाँकि, आप एक प्रतिबंधित पॉइंटर को एक अप्रतिबंधित पॉइंटर पर ठीक से असाइन कर सकते हैं:
void update_ptrs_v2( int *restrict p, int *restrict q, int const *restrict v ) { *p = *v; *q = *v; }हालांकि पी अप्रतिबंधित है, फिर भी कंपाइलर वही अनुकूलन कर सकता है।
आंतरिक दायरे में एक प्रतिबंधित सूचक को बाहरी दायरे में दूसरे को निर्दिष्ट करना भी ठीक है (लेकिन इसके विपरीत नहीं):
void update_ptrs_v2( int *restrict p, int *restrict q, int const *restrict v ) { *p = *v; *q = *v; }कब (और कब नहीं) उपयोग प्रतिबंधित करें
महत्वपूर्ण प्रदर्शन में सुधार करता है। प्रतिबंध के दुरुपयोग के कारण होने वाले बग का निदान करना बहुत कठिन है।
दूसरा, यदि प्रतिबंधित का उपयोग एक फ़ंक्शन के कार्यान्वयन तक ही सीमित है जहां प्रतिबंधित पॉइंटर्स के माध्यम से एक्सेस की गई मेमोरी कोआप द्वारा आवंटित किया गया था, तो यह अधिक सुरक्षित है। उदाहरण के लिए, दिया गया:
void update_ptrs_v2( int *restrict p, int *restrict q, int const *restrict v ) { *p = *v; *q = *v; }कोड सरणी के पहले और दूसरे हिस्सों पर सुरक्षित रूप से काम कर सकता है क्योंकि वे ओवरलैप नहीं होते हैं (मान लें कि आप कभी भी आधे_1st[n/2] या उससे आगे तक नहीं पहुंचते हैं)।
तीसरा, यदि किसी फ़ंक्शन के पैरामीटर में प्रतिबंधित का उपयोग किया जाता है, तो यह संभावित रूप से
कम सुरक्षित है। उदाहरण के लिए, सुरक्षित() को update_ptrs_v2() से कंट्रास्ट करें जहां कॉलर पॉइंटर्स को नियंत्रित करता है। जैसा कि आप जानते हैं, कॉल करने वाले ने इसे गलत समझा और उस उपनाम को इंगित कर दिया।
मिश्रितकेवल ऑब्जेक्ट (या शून्य) के पॉइंटर्स को प्रतिबंधित के साथ योग्य बनाया जा सकता है:
void update_ptrs_v2( int *restrict p, int *restrict q, int const *restrict v ) { *p = *v; *q = *v; }आप संरचना सदस्यों के लिए प्रतिबंधित का उपयोग कर सकते हैं, उदाहरण के लिए:
void update_ptrs_v2( int *restrict p, int *restrict q, int const *restrict v ) { *p = *v; *q = *v; }कहता है कि डेटा उस डेटा का एकमात्र सूचक होगा और बाएं और दाएं कभी भी एक ही नोड को इंगित नहीं करेंगे। हालाँकि, संरचना सदस्यों के लिए प्रतिबंधित का उपयोग करना बहुत असामान्य है।
अंत में, C के पास
नहीं है। क्यों नहीं? इसका एक लंबा उत्तर है, लेकिन TL;DR संस्करण यह है:
निष्कर्ष
बुद्धिमानी से उपयोग करें।
अस्वीकरण: उपलब्ध कराए गए सभी संसाधन आंशिक रूप से इंटरनेट से हैं। यदि आपके कॉपीराइट या अन्य अधिकारों और हितों का कोई उल्लंघन होता है, तो कृपया विस्तृत कारण बताएं और कॉपीराइट या अधिकारों और हितों का प्रमाण प्रदान करें और फिर इसे ईमेल पर भेजें: [email protected] हम इसे आपके लिए यथाशीघ्र संभालेंगे।
Copyright© 2022 湘ICP备2022001581号-3