क्या बिना किसी पांसे के एक सूचक को बाहर रखना सुरक्षित है? [डुप्लिकेट]

Jan 10 2021

क्या आगे अंकगणित के लिए सी में एक पॉइंटर आउट-ऑफ-बाउंड्स (इसे डीफ़ेरिंग किए बिना) रखना सुरक्षित है?

void f(int *array)
{
    int *i = array - 1;  // OOB

    while(...) {
        ++i;
        ...
    }
}

void g(int *array, int *end /* past-the-end pointer: OOB */)
{
    while(array != end) {
        ...
        ++array;
    }
}

मैं कुछ चरम मामलों की कल्पना करता हूं, अगर पता स्मृति का पहला या आखिरी है ...

जवाब

9 MikeCAT Jan 10 2021 at 07:31

पिछले तत्व के पिछले तत्व के लिए पॉइंटर को ले जाने की अनुमति है, लेकिन पहले तत्व को आगे बढ़ने या आगे बढ़ने की अनुमति नहीं है।

N1570 6.5.6 एडिटिव ऑपरेटरों से उद्धरण (बिंदु 8):

जब किसी सूचक का पूर्णांक प्रकार एक सूचक से जोड़ा या घटाया जाता है, तो परिणाम में सूचक ऑपरेटर का प्रकार होता है। यदि पॉइंटर ऑपरेटर किसी सरणी ऑब्जेक्ट के तत्व को इंगित करता है, और सरणी काफी बड़ा है, तो परिणाम एक तत्व को मूल तत्व से ऑफसेट करता है जैसे कि परिणामी और मूल सरणी तत्वों के अंशों का अंतर पूर्णांक अभिव्यक्ति के बराबर होता है। दूसरे शब्दों में, यदि अभिव्यक्ति P किसी सरणी वस्तु के i-th तत्व की ओर इंगित करता है, तो भाव (P) + N (समकक्ष, N + (P)) और (P) -N (जहाँ N का मान n है) बिंदु है , क्रमशः, सरणी वस्तु के i + n-th और i th n-th तत्व, बशर्ते वे मौजूद हों। इसके अलावा, यदि अभिव्यक्ति P किसी सरणी वस्तु के अंतिम तत्व की ओर इंगित करता है, तो अभिव्यक्ति (P) +1 सरणी वस्तु के अंतिम तत्व के पिछले अंक को इंगित करता है, और यदि अभिव्यक्ति Q किसी सरणी ऑब्जेक्ट के अंतिम तत्व को इंगित करता है। एक्सप्रेशन ऑब्जेक्ट के अंतिम तत्व को अभिव्यक्ति (क्यू) -1 अंक। यदि पॉइंटर ऑपरेटर और परिणाम एक ही एरे ऑब्जेक्ट के तत्वों को इंगित करते हैं, या एरे ऑब्जेक्ट के अंतिम तत्व के पिछले एक, मूल्यांकन एक अतिप्रवाह का उत्पादन नहीं करेगा; अन्यथा, व्यवहार अपरिभाषित है। यदि परिणाम सरणी ऑब्जेक्ट के अंतिम तत्व को एक पिछले अंक देता है, तो इसका उपयोग मूल्यांकन किए जाने वाले एक यूनिरी * ऑपरेटर के ऑपरेटर के रूप में नहीं किया जाएगा।

3 dbush Jan 10 2021 at 07:32

एक पॉइंटर सरणी के अंतिम तत्व के एक तत्व को इंगित कर सकता है, और पॉइंटर अंकगणित उस पॉइंटर और सरणी के एक तत्व के लिए एक पॉइंटर के बीच किया जा सकता है।

इस तरह के एक पॉइंटर को डिफरेंस नहीं किया जा सकता है, लेकिन इसका इस्तेमाल पॉइंटर अंकगणित में किया जा सकता है। उदाहरण के लिए, निम्नलिखित मान्य है:

char arr[10];
char *p1, *p2;
p1 = arr + 10;
p2 = arr + 5;
int diff = p1 - p2;
printf("diff=%d\n", diff);   // prints 5

एक पॉइंटर पहले तत्व से पहले इंगित नहीं हो सकता है ।

इसे सी मानक के खंड ६.५.६88 में लिखा गया है :

जब पूर्णांक प्रकार की अभिव्यक्ति को पॉइंटर से जोड़ा या घटाया जाता है, तो परिणाम में पॉइंटर ऑपरेटर का प्रकार होता है। यदि पॉइंटर ऑपरेटर किसी सरणी ऑब्जेक्ट के तत्व को इंगित करता है, और सरणी काफी बड़ा है, तो परिणाम एक तत्व को मूल तत्व से ऑफसेट करता है जैसे कि परिणामी और मूल सरणी तत्वों के अंशों का अंतर पूर्णांक अभिव्यक्ति के बराबर होता है। दूसरे शब्दों में, यदि अभिव्यक्ति किसी सरणी वस्तु Pके i -th तत्व की ओर इंगित करता है , तो अभिव्यक्तियाँ (P)+N(समतुल्य N+(P)) , और (P)-N(जहाँ Nमान n है ) को क्रमशः, i + n -th और i-n -th को इंगित करता है। सरणी ऑब्जेक्ट के तत्व, बशर्ते वे मौजूद हों। इसके अलावा, यदि अभिव्यक्ति Pकिसी सरणी ऑब्जेक्ट के अंतिम तत्व को (P)+1इंगित करता है , तो अभिव्यक्ति सरणी ऑब्जेक्ट के अंतिम तत्व को इंगित करता है, और यदि अभिव्यक्ति Qकिसी सरणी ऑब्जेक्ट (Q)-1के अंतिम तत्व को इंगित करता है, तो अभिव्यक्ति अंतिम तत्व को इंगित करता है। सरणी वस्तु का। यदि पॉइंटर ऑपरेटर और परिणाम एक ही एरे ऑब्जेक्ट के तत्वों को इंगित करते हैं, या एरे ऑब्जेक्ट के अंतिम तत्व के पिछले एक, मूल्यांकन एक अतिप्रवाह का उत्पादन नहीं करेगा; अन्यथा, व्यवहार अपरिभाषित है। यदि परिणाम सरणी ऑब्जेक्ट के अंतिम तत्व को एक पिछले अंक देता है, तो इसका उपयोग *मूल्यांकन किए जाने वाले एक अपर ऑपरेटर के ऑपरेंड के रूप में नहीं किया जाएगा ।

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

klutt Jan 10 2021 at 08:19

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

void foo(int *arr, int *end) {
    while(end-- != arr) { // Ouch, bad idea...
        // Code
    }
    // Here, end has the value arr[-1]
}

ऐसा इसलिए, क्योंकि जब endएक ही तत्व के बिंदुओं के रूप में arr, स्थिति झूठी होगी, लेकिन उसके बाद , endएक बार फिर से घटाया जाता है और सरणी से पहले एक तत्व को इंगित करेगा , इस प्रकार अपरिभाषित व्यवहार को लागू करेगा।

ध्यान दें कि इसके अलावा, कोड ठीक काम करता है। बग को ठीक करने के लिए, आप इसके बजाय ऐसा कर सकते हैं:

void foo(int *arr, int *end) {
    while(end != arr) { 
        end--; // Move end-- to inside the loop, in the very beginning

        // Code
    }

    // And here, end is equal to arr, which is perfectly fine
}

लूप में कोड पहले की तरह ही काम करेगा। अंतर केवल इतना है कि endपिछली बार नहीं घटाया जाएगा।