क्या बिना किसी पांसे के एक सूचक को बाहर रखना सुरक्षित है? [डुप्लिकेट]
क्या आगे अंकगणित के लिए सी में एक पॉइंटर आउट-ऑफ-बाउंड्स (इसे डीफ़ेरिंग किए बिना) रखना सुरक्षित है?
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;
}
}
मैं कुछ चरम मामलों की कल्पना करता हूं, अगर पता स्मृति का पहला या आखिरी है ...
जवाब
पिछले तत्व के पिछले तत्व के लिए पॉइंटर को ले जाने की अनुमति है, लेकिन पहले तत्व को आगे बढ़ने या आगे बढ़ने की अनुमति नहीं है।
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 अंक। यदि पॉइंटर ऑपरेटर और परिणाम एक ही एरे ऑब्जेक्ट के तत्वों को इंगित करते हैं, या एरे ऑब्जेक्ट के अंतिम तत्व के पिछले एक, मूल्यांकन एक अतिप्रवाह का उत्पादन नहीं करेगा; अन्यथा, व्यवहार अपरिभाषित है। यदि परिणाम सरणी ऑब्जेक्ट के अंतिम तत्व को एक पिछले अंक देता है, तो इसका उपयोग मूल्यांकन किए जाने वाले एक यूनिरी * ऑपरेटर के ऑपरेटर के रूप में नहीं किया जाएगा।
एक पॉइंटर सरणी के अंतिम तत्व के एक तत्व को इंगित कर सकता है, और पॉइंटर अंकगणित उस पॉइंटर और सरणी के एक तत्व के लिए एक पॉइंटर के बीच किया जा सकता है।
इस तरह के एक पॉइंटर को डिफरेंस नहीं किया जा सकता है, लेकिन इसका इस्तेमाल पॉइंटर अंकगणित में किया जा सकता है। उदाहरण के लिए, निम्नलिखित मान्य है:
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
के अंतिम तत्व को इंगित करता है, तो अभिव्यक्ति अंतिम तत्व को इंगित करता है। सरणी वस्तु का। यदि पॉइंटर ऑपरेटर और परिणाम एक ही एरे ऑब्जेक्ट के तत्वों को इंगित करते हैं, या एरे ऑब्जेक्ट के अंतिम तत्व के पिछले एक, मूल्यांकन एक अतिप्रवाह का उत्पादन नहीं करेगा; अन्यथा, व्यवहार अपरिभाषित है। यदि परिणाम सरणी ऑब्जेक्ट के अंतिम तत्व को एक पिछले अंक देता है, तो इसका उपयोग*
मूल्यांकन किए जाने वाले एक अपर ऑपरेटर के ऑपरेंड के रूप में नहीं किया जाएगा ।
ध्यान दें कि बोल्ड किए गए हिस्से में कहा गया है कि व्यूअर के अंत में एक तत्व को इंगित करने के लिए एक पॉइंटर बनाया जा सकता है, और एरे की शुरुआत से पहले किसी भी बिंदु को इंगित करने की अनुमति नहीं है।
जैसा कि दूसरों ने बताया है, आपको एक अतीत को इंगित करने की अनुमति है । लेकिन याद रखें कि पहले एक तत्व को इंगित करने की अनुमति नहीं है । इसलिए आप सावधान रहना चाहते हैं यदि आप एल्गोरिदम लिखते हैं जो ट्रैवर्स को पीछे की ओर ले जाता है। क्योंकि यह स्निपेट अमान्य है:
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
पिछली बार नहीं घटाया जाएगा।