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

सी में अस्पष्ट "प्रतिबंधित" कीवर्ड

2024-11-04 को प्रकाशित
ब्राउज़ करें:328

The Obscure “restrict” Keyword in C

परिचय

अन्य चीजों के अलावा, सी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 * प्रतिबंधित v ) { *पी = *वी; *क्यू = *वी; }
void update_ptrs_v2( int *restrict p, int *restrict q,
                     int const *restrict v ) {
  *p  = *v;
  *q  = *v;
}
(दाएं से बाएं पढ़ें, उदाहरण के लिए, v एक स्थिरांक int के लिए एक प्रतिबंधित सूचक है; या cdecl का उपयोग करें।)

प्रतिबंध जोड़कर, कंपाइलर अब इस तरह कोड उत्पन्न कर सकता है:


mov eax, [rdx] ; टीएमपी = *वी जोड़ें [rdi], eax ; *पी = टीएमपी [आरएसआई], ईएक्स जोड़ें; *क्यू = टीएमपी
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 f( int *restrict d, int *restrict s ) { int *प्रतिबंधित p = s; // अपरिभाषित व्यवहार
void update_ptrs_v2( int *restrict p, int *restrict q,
                     int const *restrict v ) {
  *p  = *v;
  *q  = *v;
}
हालाँकि, आप एक प्रतिबंधित पॉइंटर को एक अप्रतिबंधित पॉइंटर पर ठीक से असाइन कर सकते हैं:


void f( int *restrict d, int *restrict s ) { पूर्णांक *पी = एस; // ठीक है
void update_ptrs_v2( int *restrict p, int *restrict q,
                     int const *restrict v ) {
  *p  = *v;
  *q  = *v;
}
हालांकि पी अप्रतिबंधित है, फिर भी कंपाइलर वही अनुकूलन कर सकता है।

आंतरिक दायरे में एक प्रतिबंधित सूचक को बाहरी दायरे में दूसरे को निर्दिष्ट करना भी ठीक है (लेकिन इसके विपरीत नहीं):


void f( int *restrict d, int *restrict s ) { {//आंतरिक दायरा int *प्रतिबंधित p = s; // ठीक है //... एस = पी; // अपरिभाषित व्यवहार } }
void update_ptrs_v2( int *restrict p, int *restrict q,
                     int const *restrict v ) {
  *p  = *v;
  *q  = *v;
}
कब (और कब नहीं) उपयोग प्रतिबंधित करें

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

महत्वपूर्ण प्रदर्शन में सुधार करता है। प्रतिबंध के दुरुपयोग के कारण होने वाले बग का निदान करना बहुत कठिन है।

दूसरा, यदि प्रतिबंधित का उपयोग एक फ़ंक्शन के कार्यान्वयन तक ही सीमित है जहां प्रतिबंधित पॉइंटर्स के माध्यम से एक्सेस की गई मेमोरी को

आप द्वारा आवंटित किया गया था, तो यह अधिक सुरक्षित है। उदाहरण के लिए, दिया गया:

शून्य सुरक्षित (अहस्ताक्षरित एन) { एन = एन % 2 != 0; // गोल करके सम बनाएं int *const array = Malloc(n * sizeof(अहस्ताक्षरित)); अहस्ताक्षरित *आधे_प्रथम को प्रतिबंधित करें = सरणी; अहस्ताक्षरित *आधे_2 को प्रतिबंधित करें = सरणी n/2; //... मुफ़्त (सरणी); }
void update_ptrs_v2( int *restrict p, int *restrict q,
                     int const *restrict v ) {
  *p  = *v;
  *q  = *v;
}
कोड सरणी के पहले और दूसरे हिस्सों पर सुरक्षित रूप से काम कर सकता है क्योंकि वे ओवरलैप नहीं होते हैं (मान लें कि आप कभी भी आधे_1st[n/2] या उससे आगे तक नहीं पहुंचते हैं)।

तीसरा, यदि किसी फ़ंक्शन के पैरामीटर में प्रतिबंधित का उपयोग किया जाता है, तो यह संभावित रूप से

कम सुरक्षित है। उदाहरण के लिए, सुरक्षित() को update_ptrs_v2() से कंट्रास्ट करें जहां कॉलर पॉइंटर्स को नियंत्रित करता है। जैसा कि आप जानते हैं, कॉल करने वाले ने इसे गलत समझा और उस उपनाम को इंगित कर दिया।

मिश्रित

केवल ऑब्जेक्ट (या शून्य) के पॉइंटर्स को प्रतिबंधित के साथ योग्य बनाया जा सकता है:

int x प्रतिबंधित करें; // त्रुटि: ऑब्जेक्ट को प्रतिबंधित नहीं किया जा सकता पूर्णांक प्रतिबंधित *पी; // त्रुटि: ऑब्जेक्ट को प्रतिबंधित करने के लिए सूचक int (*प्रतिबंधित f)(); // त्रुटि: पॉइंटर-टू-फंक्शन
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 संस्करण यह है:

    यह मुश्किल से मिलने वाले बग का एक स्रोत हो सकता है जिसे सी समिति सी से आयात नहीं करना चाहती थी।
  • सी के पॉइंटर्स के बढ़ते उपयोग, जैसे, यह, सुरक्षित रूप से प्रतिबंधित करना और भी कठिन बना देता है।
हालाँकि, कई कंपाइलर्स के पास एक्सटेंशन के रूप में __restrict__ है।

निष्कर्ष

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

बुद्धिमानी से उपयोग करें।

विज्ञप्ति वक्तव्य यह आलेख यहां पुन: प्रस्तुत किया गया है: https://dev.to/pauljlucas/the-obscure-restrict-keyword-in-c-2541 यदि कोई उल्लंघन है, तो कृपया इसे हटाने के लिए [email protected] से संपर्क करें।
नवीनतम ट्यूटोरियल अधिक>

चीनी भाषा का अध्ययन करें

अस्वीकरण: उपलब्ध कराए गए सभी संसाधन आंशिक रूप से इंटरनेट से हैं। यदि आपके कॉपीराइट या अन्य अधिकारों और हितों का कोई उल्लंघन होता है, तो कृपया विस्तृत कारण बताएं और कॉपीराइट या अधिकारों और हितों का प्रमाण प्रदान करें और फिर इसे ईमेल पर भेजें: [email protected] हम इसे आपके लिए यथाशीघ्र संभालेंगे।

Copyright© 2022 湘ICP备2022001581号-3