पॉइंटर की चाल और ऐरे की सीमाएँ
किसी वैध स्थिति पर तत्व होना आवश्यक नहीं है
इंडेक्स बताता है कि ऐरे का कौन-सा तत्व चुनना है। पॉइंटर उसी तत्व को पहचान सकता है, और पॉइंटर को आगे बढ़ाने से चुनी हुई स्थिति बदलती है, रखे हुए पूर्णांक नहीं। एक महत्वपूर्ण सीमा समझें: पॉइंटर अंतिम तत्व से एक स्थान आगे तक पहुँच सकता है, लेकिन वहाँ पढ़ने या लिखने के लिए कोई तत्व नहीं होता।
इस पाठ में आप इंडेक्स वाले और पॉइंटर वाले स्कैन का मेल समझेंगे, पॉइंटर की चाल को किसी तत्व में लेखन से अलग पहचानेंगे, सीमा संबंधी ऑपरेशन का वर्गीकरण करेंगे और sizeof से वास्तविक ऐरे के तत्व गिनेंगे। आप यह भी समझाएँगे कि ऐरे-पैरामीटर वाले सहायक फ़ंक्शन के भीतर वही आकार-गणना कॉलर के ऐरे की लंबाई क्यों नहीं निकाल सकती।
पहले से आवश्यक बातें: इनिशियलाइज़ किए ऐरे, वैध इंडेक्स, सीमित लूप और संचयक, पॉइंटर के लक्ष्य और पॉइंटर मान की कॉपी, तथा पिछले पाठ के ऐरे आर्ग्युमेंट, समायोजित पैरामीटर और स्पष्ट लंबाई-अनुबंध। पढ़ाने के लिए हम C11 चुनते हैं; GATE पाठ्यक्रम ने यह संस्करण निर्धारित नहीं किया है। यहाँ हर ऐरे का आकार धनात्मक स्थिरांक है और उसके int तत्व इनिशियलाइज़ किए गए हैं। int की सारी अंकगणित −32767 से 32767 के भीतर रहती है। पूरे प्रोग्राम स्वतंत्र हैं। केवल वर्गीकरण के लिए दिए पाठ का अर्थ किसी अवैध ऑपरेशन को कंपाइल या निष्पादित करने का निर्देश नहीं है।
हम एक जीवित ऐरे की स्थितियों से तर्क करेंगे, काल्पनिक संख्यात्मक एड्रेस से नहीं। int का बाइट आकार, पॉइंटर की चौड़ाई, सभी पॉइंटर प्रकारों का समान आकार या अलग ऑब्जेक्टों का भौतिक क्रम नहीं माना गया है। द्वि-आयामी ऐरे, मेमोरी आवंटन और कास्ट इस पाठ के बाहर हैं।
कदमों को तत्वों में गिनें
int marks[4] = {5, 2, 8, 1}; और int *p = marks; से शुरू करें। इस इनिशियलाइज़र में marks अपने पहले तत्व का पॉइंटर देता है। दर्ज करें: p → marks[0]।
p + 1का मूल्यांकनmarks[1]का पॉइंटर देता है। केवल यह व्यंजकpमें रखा मान नहीं बदलता- असाइनमेंट
p = p + 1;नई स्थिति कोpमें रखता है। किसी तत्व का मान नहीं बदलता - अलग स्टेटमेंट
p++;भी पॉइंटर को एक तत्व आगे बढ़ाता है। हम चाल को पढ़ने और लिखने से अलग रखते हैं p → marks[1]होने परp + 2,marks[3]को पहचानता है औरp - 1,marks[0]को
जो पूर्णांक जोड़ा जाता है, वह उस प्रकार के तत्व गिनता है जिसकी ओर पॉइंटर जाता है। यहाँ प्रकार int * है, इसलिए हर कदम एक int तत्व का है। कदम को किसी मान लिए गए बाइट आकार से गुणा न करें। marks[1] से 2 का ऑफ़सेट इंडेक्स 3 पर पहुँचाता है, चाहे उस कार्यान्वयन में int कितने भी बाइट का हो।
एक उपयोगी प्रतीकात्मक रिकॉर्ड है (ऐरे की पहचान, स्थिति)। उदाहरण के लिए, (marks, 1) इसी विशेष ऐरे के तत्व 1 का पॉइंटर बताता है। यह हमारे तर्क की लिखावट है, कोई C डेटा संरचना या पॉइंटर का पूर्णांक रूप नहीं।
मान लें कि वास्तविक ऐरे a में N तत्व हैं। इस पाठ में उस जीवित ऐरे के ज्ञात पॉइंटर की स्थिति j से बताई जाती है, जिसमें विशेष अंतिम सीमा N भी शामिल है। पूर्णांक ऑफ़सेट k के लिए p + k बनाना तभी वैध है जब परिणामी स्थिति j + k, 0 से N तक हो, दोनों सीमाएँ शामिल हैं। परिणाम के रास्ते पढ़ने या लिखने के लिए सीमा और कड़ी है: 0 से N - 1। तत्व तक पहुँचने पर विचार करने से पहले पॉइंटर बनाना जाँचें। अनुमत स्थितियों से बाहर जाना अपने आप में अवैध है, भले ही आप बाद में परिणाम को डीरेफ़रेंस न करें।
कोष्ठक और डीरेफ़रेंस एक ही तत्व चुनते हैं
वास्तविक ऐरे a के वैध इंडेक्स i के लिए a[i] और *(a + i) एक ही तत्व चुनते हैं। गोल कोष्ठकों का महत्व है: पहले स्थिति i का पॉइंटर बनाएँ, फिर उसके लक्ष्य तक जाएँ। दोनों रूप उस तत्व को पढ़ सकते हैं या असाइनमेंट के लिए चुन सकते हैं।
पॉइंटर p के लिए p[i] का अर्थ *(p + i) है। इसका इंडेक्स p की वर्तमान स्थिति से सापेक्ष है। यदि p → marks[1], तो p[0], marks[1] को चुनता है और p[2], marks[3] को। केवल सबस्क्रिप्ट 2 होने से वह marks[2] नहीं चुनता। परिणामी लक्ष्य अब भी उसी ऐरे का वैध तत्व होना चाहिए। समान अर्थ वाली लिखावट सीमा से बाहर की पहुँच को वैध नहीं बनाती।
जब p ऐसे इनिशियलाइज़ किए पूर्णांक की ओर हो जिसे पढ़ना और बदलना वैध है, तब ये तीन प्रभाव अलग रखें:
| व्यंजक या स्टेटमेंट | उसका प्रभाव |
|---|---|
p = p + 1; | पॉइंटर को आगे बढ़ाता है; नई स्थिति बनाने की सीमाएँ लागू हैं |
*p = *p + 1; | चुने हुए पूर्णांक को बढ़ाता है; पॉइंटर वहीं रहता है |
*(p + 1) | यहाँ अगले तत्व को पढ़ने के लिए चुनता है, यदि वह तत्व मौजूद हो; p वहीं रहता है |
मान देने वाला व्यंजक *p + 1 वर्तमान पूर्णांक में 1 जोड़ता है। यह *(p + 1) वाला व्यंजक नहीं है। p → marks[1] होने पर इनके मान क्रमशः 3 और 8 हैं। अकेला कोई भी व्यंजक अपना परिणाम सहेजता नहीं है।
अंतिम तत्व के एक आगे की स्थिति रुकने के लिए है
चार तत्वों वाले marks के लिए marks + 4 एक वैध one-past पॉइंटर है, यानी अंतिम तत्व के ठीक बाद वाली स्थिति। यह marks[3] के बाद की सीमा पहचानता है; पाँचवाँ पूर्णांक नहीं पहचानता और ऐरे का आकार नहीं बढ़ाता। हम इसे marks + 4 (end) लिखेंगे, किसी काल्पनिक तत्व तक तीर नहीं बनाएँगे।
आप यह पॉइंटर सहेज सकते हैं, ट्रैवर्सल पॉइंटर से इसकी समानता या असमानता जाँच सकते हैं और इससे पीछे किसी तत्व पर लौट सकते हैं। विशेष रूप से, (marks + 4) - 1, marks[3] को पहचानता है। इस अंतिम सीमा के पॉइंटर को डीरेफ़रेंस करके पूर्णांक पढ़ना या लिखना वैध नहीं है। marks + 5 या marks - 1 बनाना भी इस ऐरे की अनुमत चाल-सीमा से बाहर है। पास में कोई दूसरा ऑब्जेक्ट मान लेने से यह सही नहीं होगा।
हमारा आगे जाने वाला स्कैन स्थिति 0 से शुरू होता है, पढ़ने से पहले p != end जाँचता है और हर बॉडी के बाद ठीक एक कदम आगे बढ़ता है। ये बातें मिलकर प्रमाणित करती हैं कि हर बॉडी के लिए तत्व मौजूद है। केवल शर्त किसी भी पॉइंटर की वैधता जाँचने का सामान्य तरीका नहीं है। यदि शुरुआती पॉइंटर, अंतिम सीमा का पॉइंटर या चाल का नियम गलत हो, तो असमानता की जाँच उसे ठीक नहीं करती। हम असंबंधित पॉइंटरों के बीच क्रम-संबंधी तुलना नहीं करते।
हल किया हुआ उदाहरण 1 उन्हीं चार तत्वों के दो स्कैन
दोनों योग और अंतिम समानता-जाँच का परिणाम बताएँ। पॉइंटर वाला स्कैन शुरू होने से पहले इंडेक्स वाला स्कैन समाप्त हो जाता है; कोई भी स्कैन marks नहीं बदलता।
#include <stdio.h>
int main(void)
{
int marks[4] = {5, 2, 8, 1};
int indexed_total = 0;
for (int i = 0; i < 4; i++) {
indexed_total += marks[i];
}
int pointer_total = 0;
int *p = marks;
int *end = marks + 4;
while (p != end) {
pointer_total += *p;
p = p + 1;
}
printf("%d %d %d\n", indexed_total, pointer_total, p == end);
return 0;
}तालिका दोनों अलग स्कैन के समान चरणों का मेल दिखाती है। अंतिम बॉडी के बाद की स्थिति अनुमत है, हालाँकि उससे एक और पूर्णांक नहीं मिल सकता।
| पहले ही संसाधित तत्व | इंडेक्स स्कैन का अगला इंडेक्स | पॉइंटर स्कैन की अगली स्थिति | प्रत्येक स्कैन का योग |
|---|---|---|---|
| 0 | 0 | marks + 0 | 0 |
| 1 | 1 | marks + 1 | 5 |
| 2 | 2 | marks + 2 | 7 |
| 3 | 3 | marks + 3 | 15 |
| 4 | 4 | marks + 4 | 16 |
हर सफल जाँच से पहले अगली स्थिति एक तत्व होती है। बॉडी क्रम से 5, 2, 8 और 1 पढ़ती है। हर स्कैन के जाँच-बिंदु पर योग ठीक उस शुरुआती हिस्से का है जिसे अब तक संसाधित किया गया है। अंतिम अपडेट इंडेक्स 4 या पॉइंटर स्थिति 4 तक पहुँचता है। अगली जाँच असफल होती है, इसलिए पाँचवीं बार पढ़ना नहीं होता। हर स्कैन की चार बॉडी और लूप-शर्त की पाँच जाँचें हैं।
आउटपुट है 16 16 1, फिर नई पंक्ति। समानता सही होने पर int मान 1 देती है, इसलिए %d उसके प्रकार से मेल खाता है। दोनों योग भी int हैं। ऐरे {5, 2, 8, 1} ही रहता है।
यदि आपने अंतिम मान 1 छोड़ दिया, तो संभवतः उसके बाद की सीमा के बजाय अंतिम तत्व पर ही रुक गए। यदि आप पाँचवाँ मान जोड़ने की कोशिश करते हैं, तो रुकने की स्थिति को पढ़ने योग्य तत्व मान रहे हैं। यदि p को चलाने पर आपने marks बदल दिया, तो पॉइंटर असाइनमेंट को *p के रास्ते लेखन समझ लिया।
आकारों के लिए अपना प्रकार चाहिए
sizeof ऑपरेंड के प्रकार के आधार पर C बाइटों में आकार देता है। यह ऑपरेटर है, फ़ंक्शन कॉल नहीं। हमारे स्थिर आकार वाले ऐरे और साधारण पूर्णांक या पॉइंटर ऑपरेंड के लिए इसका ऑपरेंड मूल्यांकित नहीं होता। हम परिवर्तनीय लंबाई वाले ऐरे नहीं लेते, जिनके नियम अलग से समझने होंगे।
इन परिणामों को सहेजने से पहले size_t समझें। यह <stddef.h> में उपलब्ध unsigned पूर्णांक प्रकार है और sizeof के परिणाम का प्रकार है। size_t मान छापने के लिए %zu उपयोग करें। इसे int न मानें और इसके लिए %d न लें। हमारी गिनतियाँ छोटी और गैर-ऋणात्मक हैं; size_t लूप इंडेक्स को size_t लंबाई के साथ रखें। unsigned गिनती में −1 को नहीं-मिला संकेत की तरह न रखें। किसी गिनती से 1 घटाने से पहले उसका धनात्मक होना स्थापित करें।
main में वास्तविक ऐरे घोषणा int values[5] = {4, 7, 1, 6, 2}; के लिए sizeof values पूरे ऐरे को मापता है, जबकि sizeof values[0] एक int तत्व को। इस sizeof का ऑपरेंड बनने पर ऐरे पॉइंटर में नहीं बदलता। यदि एक तत्व S C बाइट लेता है, तो पूरा ऐरे 5 * S लेता है; अनुपात 5 है। हमें S का संख्यात्मक मान नहीं चाहिए।
इसलिए size_t count = sizeof values / sizeof values[0]; में 5 दर्ज होता है। अनुपात तत्वों की संख्या है, बाइटों की नहीं। यह उस स्थान पर वास्तविक ऐरे का वर्णन करता है जहाँ उसका ऐरे प्रकार उपलब्ध है। यह सामान्य रूप से “यह नाम जिसकी ओर जाता है उसकी लंबाई” निकालने का तरीका नहीं है।
हल किया हुआ उदाहरण 2 वास्तविक ऐरे की गिनती और चुनी हुई लंबाई
यह सहायक फ़ंक्शन पिछले पाठ का ऐरे-पैरामीटर रूप इस्तेमाल करता है, अब size_t लंबाई के साथ। पैरामीटर values का प्रकार समायोजित होकर int * होता है। इस उदाहरण का अनुबंध है: कॉलर ऐसे जीवित ऐरे का पहला तत्व देता है जिसमें अधिकतम पाँच इनिशियलाइज़ किए पूर्णांक हों, हर मान 0 से 20 तक हो और length, शून्य से ऐरे की वास्तविक तत्व-संख्या तक हो। सहायक फ़ंक्शन वह शुरुआती हिस्सा पढ़ता है, कोई तत्व नहीं बदलता और 0 से 100 तक का int योग लौटाता है। शून्य लंबाई पर भी हमारा अनुबंध वास्तविक ऐरे का पहला तत्व देता है; इसमें नल-पॉइंटर कॉल शामिल नहीं हैं।
#include <stddef.h>
#include <stdio.h>
int sum_prefix(int values[], size_t length)
{
int total = 0;
for (size_t i = 0; i < length; i++) {
total += values[i];
}
return total;
}
int main(void)
{
int values[5] = {4, 7, 1, 6, 2};
size_t count = sizeof values / sizeof values[0];
size_t selected = 3;
int prefix_total = sum_prefix(values, selected);
int full_total = sum_prefix(values, count);
printf("%zu %zu %d %d\n", count, selected, prefix_total, full_total);
return 0;
}| कॉल | दी गई लंबाई | देखे गए इंडेक्स | क्रमिक योग | लौटाया योग |
|---|---|---|---|---|
sum_prefix(values, selected) | 3 | 0, 1, 2 | 4, 11, 12 | 12 |
sum_prefix(values, count) | 5 | 0, 1, 2, 3, 4 | 4, 11, 12, 18, 20 | 20 |
पहली कॉल पाँच मौजूद तत्वों में से केवल तीन चुनती है। दूसरी सभी पाँच चुनती है। हर बॉडी i < length का पालन करती है और कॉलर की दी हुई लंबाई भी क्षमता की सीमा में है। दोनों योग अलग-अलग 0 से शुरू होते हैं। values के रास्ते कहीं लेखन नहीं है, इसलिए कॉलर का ऐरे अपरिवर्तित रहता है। आउटपुट: 5 3 12 20, फिर नई पंक्ति।
अब पूछें कि हर स्कोप में sizeof values का क्या अर्थ होगा:
mainमेंvaluesवास्तविक पाँच-तत्वों वाला ऐरे है, इसलिए उसका आकार सभी पाँच तत्वों को शामिल करता हैsum_prefixके भीतरvaluesपॉइंटर पैरामीटर है, इसलिए उसका आकार उसint *पैरामीटर प्रकार का होगा। पैरामीटर घोषणा में वर्ग कोष्ठक लिखने से कॉलर का ऐरे प्रकार सुरक्षित नहीं रहता- उस सहायक फ़ंक्शन में
sizeof values[0]अब भीintको मापेगा। पॉइंटर के आकार को तत्व के आकार से भाग देने का दी गई लंबाई या क्षमता से कोई सामान्य संबंध नहीं है
ये प्रकारों से जुड़े तथ्य हैं, यह दावा नहीं कि दोनों बाइट आकारों का अलग होना अनिवार्य है। किसी मशीन पर संख्यात्मक संयोग कोई लंबाई-नियम सिद्ध नहीं करता। पॉइंटर पैरामीटर अपने आप ऐरे-लंबाई का अलग फ़ील्ड नहीं रखता। length की जगह वह अनुपात रखने से स्पष्ट अनुबंध छूट जाएगा। निश्चित व्यवहार वाले इस प्रोग्राम में ऐसा प्रतिस्थापन नहीं है।
दो स्थितियों की दूरी का भी प्रकार होता है
उसी जीवित ऐरे के दो उपयुक्त पॉइंटरों को घटाने से तत्वों में signed दूरी मिल सकती है। एक या दोनों पॉइंटर उस ऐरे की one-past स्थिति पर हो सकते हैं। परिणाम को <stddef.h> में घोषित signed पूर्णांक प्रकार ptrdiff_t में समाना चाहिए। उसे छापने के लिए %td लें। हमारी दूरियाँ केवल −5 से 5 तक हैं और इस प्रकार में समाती हैं। किसी सामान्य पॉइंटर-अंतर को इस धारणा से न सहेजें या छापें कि उसका प्रकार आवश्यक रूप से int या size_t है।
स्थितियाँ j और k होने पर पहले पॉइंटर में से दूसरा घटाने का परिणाम j - k है। क्रम महत्वपूर्ण है: अंत में से आरंभ घटाना धनात्मक, आरंभ में से अंत घटाना ऋणात्मक और किसी स्थिति में से वही स्थिति घटाना शून्य देता है। यह तत्वों के कदम गिनता है, बाइट नहीं। हम अलग ऐरे के पॉइंटर नहीं घटाते, भले दोनों ऐरे के मान समान हों या उनकी घोषणाएँ साथ लिखी हों। यह पाठ पॉइंटर घटाव का उपयोग यह पता लगाने के लिए नहीं करता कि दो अज्ञात पॉइंटर एक ही ऐरे के हैं या नहीं।
हल देखने से पहले अभ्यास करें
ये चार मौलिक ट्रांसफ़र अभ्यास बिना अंक के हैं। संख्यात्मक उत्तर के साथ प्रकार और सीमाओं के तथ्य भी बताएँ। नीचे पाठ-ब्लॉक में दिए दोनों समूह केवल वर्गीकरण के लिए हैं; उनके अवैध ऑपरेशन कभी कंपाइल या निष्पादित न करें।
अभ्यास 1 स्थितियों का वर्गीकरण और पूरी दूरी
मान लें कि एक जीवित इनिशियलाइज़ किया ऐरे int a[4] = {9, 3, 7, 2}; मौजूद है। हर व्यंजक का स्वतंत्र वर्गीकरण करें: वैध तत्व-पॉइंटर, वैध अंतिम-सीमा पॉइंटर, वैध पूर्णांक पठन या अपरिभाषित ऑपरेशन। ये क्रम से चलने वाले स्टेटमेंट नहीं हैं।
a + 0 a + 4 *(a + 3) *(a + 4) a + 5 a - 1
फिर हल में दिए सुरक्षित पूरे प्रोग्राम का आउटपुट बताएँ। देखने से पहले first = a और end = a + 4 मानकर अंतिम तत्व के मान, end - first और first - end पर तर्क करें। दोनों दूरियों के लिए कौन-सा प्रकार और प्रिंट फ़ॉर्मैट चाहिए?
अभ्यास 2 एक पॉइंटर चलाएँ और दो तत्व बदलें
वर्तमान स्थितियों का ट्रेस ऐरे के मानों से अलग करें। समझाएँ कि q के चलने के बाद q[0] क्या चुनता है।
#include <stdio.h>
int main(void)
{
int a[5] = {2, 5, 8, 4, 1};
int *p = a + 1;
int *q = p;
p = p + 2;
*p = *q + 3;
q = q + 1;
q[0] = *p - 2;
printf("%d %d %d %d %d | %d %d\n",
a[0], a[1], a[2], a[3], a[4], *p, *q);
return 0;
}अभ्यास 3 पीछे जाने वाला स्कैन ठीक करें
लक्ष्य तीनों तत्वों को अंतिम से पहले की ओर देखते हुए उनका योग निकालना है। यह असुरक्षित अंश केवल वर्गीकरण के लिए है।
int values[3] = {3, 4, 6};
int *p = values + 3;
int total = 0;
while (p != values) {
total += *p;
p = p - 1;
}पहला अवैध ऑपरेशन पहचानें। बॉडी के केवल दो स्टेटमेंटों का क्रम बदलकर सुधारें, फिर देखने का क्रम, क्रमिक योग और अंतिम पॉइंटर स्थिति बताएँ। समझाएँ कि सुधरी हुई बॉडी पहले तत्व से पहले का पॉइंटर कभी क्यों नहीं बनाती। यदि शुरुआत p = values से होती, तो क्या सुधरा हुआ लूप कुछ पढ़ता?
अभ्यास 4 लंबाई गढ़े बिना अंतिम मान निकालें
एक सहायक फ़ंक्शन की घोषणा int final_value(int data[], size_t length) है। इसके अनुबंध में जीवित इनिशियलाइज़ किए int ऐरे के पहले तत्व का पॉइंटर और कम-से-कम 1 तथा वास्तविक गिनती से अधिक नहीं चुनी गई लंबाई चाहिए। यह अंतिम चुना हुआ पूर्णांक लौटाता है और कोई तत्व नहीं बदलता।
main में int data[5] = {2, 4, 6, 8, 9}; के लिए वास्तविक ऐरे से गिनती निकालकर कॉल पूरी करें। समझाएँ कि सहायक फ़ंक्शन के भीतर स्पष्ट लंबाई की जगह sizeof data / sizeof data[0] रखना उचित क्यों नहीं है। क्या किसी मशीन पर देखा गया पॉइंटर आकार तत्वों की संख्या साबित करता है? समझाएँ कि data[length - 1] के लिए धनात्मक लंबाई की शर्त क्यों चाहिए।
पूरे हल और गलत रास्तों की प्रतिक्रिया
अभ्यास 1 का हल
a + 0,a[0]का वैध पॉइंटर हैa + 4वैध one-past पॉइंटर है; इससे पूर्णांक पढ़ना या लिखना संभव नहीं है*(a + 3)अंतिम तत्व का वैध पठन है और 2 देता है*(a + 4)one-past पॉइंटर का मूल्यांकित डीरेफ़रेंस करने का प्रयास है: अपरिभाषित व्यवहार, जिसका कोई निर्धारित पूर्णांक परिणाम नहीं हैa + 5में पॉइंटर बनाना ही अवैध है: बाद में डीरेफ़रेंस किए बिना भी अपरिभाषित व्यवहारa - 1भी अवैध पॉइंटर निर्माण है: पहले तत्व से पहले कोई अनुमत स्थिति नहीं है
नीचे का सुरक्षित प्रोग्राम केवल अनुमत सिरों का उपयोग करता है और पढ़ने से पहले पीछे चलता है। forward और backward, ptrdiff_t हैं, जबकि तत्व का पढ़ा गया मान int है।
#include <stddef.h>
#include <stdio.h>
int main(void)
{
int a[4] = {9, 3, 7, 2};
int *first = a;
int *end = a + 4;
int *last = end - 1;
ptrdiff_t forward = end - first;
ptrdiff_t backward = first - end;
printf("%d %td %td\n", *last, forward, backward);
return 0;
}स्थितियाँ हैं: first के लिए 0, end के लिए 4 और last के लिए 3। अंतर हैं 4 - 0 = 4 और 0 - 4 = -4। आउटपुट: 2 4 -4, फिर नई पंक्ति। ऐरे अपरिवर्तित है।
दूरी 3 बताना अंतिम इंडेक्स को अंतिम सीमा समझ लेना है। 4 को किसी मान लिए बाइट आकार से गुणा करना किसी और प्रश्न का उत्तर देता है। a + 5 को इस कारण हानिरहित मानना कि उसे पढ़ा नहीं गया, पॉइंटर निर्माण की अपनी सीमाएँ भूलना है। कोई परीक्षण रन अवैध व्यंजकों का अनिवार्य उत्तर नहीं दे सकता।
अभ्यास 2 का हल
| इस चरण के बाद | ऐरे के मान | p की स्थिति | q की स्थिति |
|---|---|---|---|
| सभी घोषणाएँ | {2, 5, 8, 4, 1} | 1 | 1 |
p = p + 2 | {2, 5, 8, 4, 1} | 3 | 1 |
*p = *q + 3 | {2, 5, 8, 8, 1} | 3 | 1 |
q = q + 1 | {2, 5, 8, 8, 1} | 3 | 2 |
q[0] = *p - 2 | {2, 5, 6, 8, 1} | 3 | 2 |
पहली चाल केवल p बदलती है; कॉपी किए पॉइंटर मान स्वतंत्र हैं। पहला लेखन q से 5 पढ़ता है और p के रास्ते a[3] में 8 लिखता है। फिर q स्थिति 2 पर जाता है। उसका सबस्क्रिप्ट 0 उसी स्थिति से सापेक्ष है, इसलिए q[0], a[2] चुनता है, a[0] नहीं। अंतिम लेखन a[3] का वर्तमान 8 पढ़ता है, 2 घटाता है और a[2] में 6 रखता है।
पढ़ने और लिखने के लिए इस्तेमाल सारी पॉइंटर स्थितियाँ 1, 2 या 3 हैं, जो पाँच-तत्वों वाले ऐरे के भीतर हैं। आउटपुट: 2 5 6 8 1 | 8 6, फिर नई पंक्ति। a[0] में बदलाव बताना q को अब भी ऐरे के आरंभ का नाम मान लेना है। p के साथ q को भी चलाना कॉपी किए पॉइंटर मान को स्थायी जुड़ाव समझने की गलती होगी।
अभ्यास 3 का हल
values + 3 बनाना वैध है। पहली लूप-जाँच सही है, लेकिन मूल बॉडी का पहला स्टेटमेंट उसी अंतिम सीमा के पॉइंटर से पढ़ने की कोशिश करता है। उस पठन का व्यवहार अपरिभाषित है। C इस असुरक्षित अंश के लिए कोई योग निर्धारित नहीं करता।
पढ़ने से पहले पीछे चलें:
#include <stdio.h>
int main(void)
{
int values[3] = {3, 4, 6};
int *p = values + 3;
int total = 0;
while (p != values) {
p = p - 1;
total += *p;
}
printf("%d %d\n", total, p == values);
return 0;
}| बॉडी संख्या | बॉडी से पहले स्थिति | चलने के बाद पढ़ी स्थिति | पढ़ा मान | बॉडी के बाद योग |
|---|---|---|---|---|
| 1 | 3 | 2 | 6 | 6 |
| 2 | 2 | 1 | 4 | 10 |
| 3 | 1 | 0 | 3 | 13 |
हर सफल जाँच पर वर्तमान स्थिति 1 से 3 के बीच है। पीछे जाने पर 0 से 2 के बीच पढ़ने योग्य स्थिति मिलती है। तीसरी बॉडी के बाद p, values के बराबर है; फिर घटाने से पहले ही अगली जाँच असफल हो जाती है। इसलिए लूप कभी स्थिति −1 नहीं बनाता। इसकी तीन बॉडी और चार जाँचें हैं। आउटपुट: 13 1, फिर नई पंक्ति; ऐरे अपरिवर्तित है।
यदि सुधरा लूप values से शुरू हो, तो पहली जाँच ही असफल है: कोई बॉडी नहीं, चाल नहीं, पठन नहीं, योग 0। वास्तविक तीन-तत्वों वाला ऐरे अब भी मौजूद है। शर्त पर दोबारा विचार किए बिना मूल शुरुआती पॉइंटर को केवल अंतिम तत्व पर रखने से पहला तत्व छूट जाएगा। किसी संभव लगने वाले योग से अनुमान लगाने के बजाय शुरुआती स्थिति, जाँच, चाल और पठन का आपसी संबंध सुधारें।
अभ्यास 4 का हल
#include <stddef.h>
#include <stdio.h>
int final_value(int data[], size_t length)
{
return data[length - 1];
}
int main(void)
{
int data[5] = {2, 4, 6, 8, 9};
size_t length = sizeof data / sizeof data[0];
int answer = final_value(data, length);
printf("%zu %d\n", length, answer);
return 0;
}main में अनुपात 5 * S / S = 5 है, जहाँ S, sizeof data[0] को दर्शाता है। कॉल पहले तत्व का पॉइंटर और गिनती 5 की कॉपी देती है। सहायक फ़ंक्शन में length - 1, 4 है, जो वैध इंडेक्स है, और वहाँ इनिशियलाइज़ किया मान 9 है। आउटपुट: 5 9, फिर नई पंक्ति। ऐरे और कॉलर की गिनती दोनों अपरिवर्तित रहते हैं।
सहायक फ़ंक्शन के भीतर data का समायोजित प्रकार int * है। उसका आकार उसी पॉइंटर प्रकार को मापता है, जबकि sizeof data[0], int को मापता है। उनका अनुपात चुनी हुई लंबाई या ऐरे की क्षमता नहीं खोजता। दो विशेष कॉलों में पॉइंटर आकार समान होने से उनके ऐरे बराबर लंबे नहीं हो जाते; इसी सहायक फ़ंक्शन को उपयुक्त तीन-तत्वों वाला ऐरे भी दिया जा सकता है। हम सभी पॉइंटर प्रकारों का आकार समान होने का दावा नहीं करते।
अनुबंध का length >= 1 अंतिम चुने तत्व का अस्तित्व स्थापित करता है और 1 घटाना उचित बनाता है। शून्य गिनती इस सहायक फ़ंक्शन के अनुबंध को तोड़ती है; परिणाम देखने के लिए तत्व तक पहुँचने का मूल्यांकन न करें। क्षमता के भीतर धनात्मक गिनती के लिए इंडेक्स 0 से क्षमता घटा 1 तक है। इसी ऐरे को लंबाई 3 देने पर इंडेक्स 2 चुना जाएगा और 6 लौटेगा। कॉलर का आकार-अनुपात उसकी क्षमता देता है; छोटा चुना हुआ हिस्सा चाहिए तो उसकी लंबाई जानबूझकर देनी होगी।
आगे बढ़ने से पहले
आउटपुट का अनुमान लगाने से पहले आपको हर बनाई गई स्थिति का औचित्य बताना आना चाहिए। यह भी बता सकें कि हर स्टेटमेंट पॉइंटर चलाता है या पूर्णांक बदलता है। ठीक उस sizeof व्यंजक पर यह पहचानें कि नाम वास्तविक ऐरे है या समायोजित पॉइंटर पैरामीटर। यह पाठ एक-आयामी स्थिर आकार वाले ऐरे तक सीमित है; किसी भी मनमाने पॉइंटर की वैधता का सामान्य परीक्षण नहीं देता।
अर्थ-संबंधी तथ्य-जाँच: WG14 N1570, खंड 6.3.2.1, 6.5.2.1, 6.5.3.4, 6.5.6, 6.7.6.3, 7.19 और 7.21.6.1। व्याख्याएँ, उदाहरण, ट्रेस और अभ्यास मौलिक हैं। यह उल्लेख तथ्य-जाँच का स्रोत पहचानता है; पुराने स्रोत-उपयोग का हिसाब वापस मिल जाने या सभी अधिकारों की समग्र मंज़ूरी का दावा नहीं है।
सूत्र और नियम
- ऐरे की पहचान और वर्तमान पॉइंटर स्थिति को तत्वों के मानों से अलग दर्ज करें
int *में 1 जोड़ने पर एकintतत्व आगे जाते हैं; बाइट आकार मानने की जरूरत नहीं है- केवल
p + kका मान निकालने सेpनहीं बदलता; असाइनमेंट याpको इन्क्रिमेंट करना उसमें रखा पॉइंटर मान अपडेट कर सकता है - वैध लक्ष्यों के लिए
a[i],*(a + i)चुनता है;p[i],pकी वर्तमान स्थिति से सापेक्ष है - निर्माण के लिए 0 से गिनती तक स्थितियाँ अनुमत हैं; तत्व पढ़ने या लिखने के लिए 0 से गिनती घटा 1 तक चाहिए
- one-past रुकने की वैध स्थिति है, कोई अतिरिक्त पढ़ने या लिखने योग्य तत्व नहीं
- ट्रैवर्सल की शुरुआत, जाँच और कदम को साथ प्रमाणित करें; अकेली तुलना सुरक्षा सिद्ध नहीं करती
- उसी ऐरे के पॉइंटरों का अंतर तत्वों के signed कदम गिनता है;
ptrdiff_tऔर%tdलें sizeofका परिणामsize_tहै;%zuलें और गिनती से 1 घटाने से पहले उसका धनात्मक होना जाँचें- ऐरे-आकार अनुपात केवल वास्तविक ऐरे की गिनती देता है; समायोजित पॉइंटर पैरामीटर को अब भी स्पष्ट लंबाई चाहिए
इस पाठ के नोट्स
प्रगति सहेजने के लिए साइन इन करें। साइन इन