इन्क्रीमेंट पॉइंट क्यों?


25

मैंने अभी हाल ही में C ++ सीखना शुरू किया, और अधिकांश लोगों के अनुसार (जो मैं पढ़ रहा हूं उसके अनुसार) मैं पॉइंटर्स के साथ संघर्ष कर रहा हूं।

पारंपरिक अर्थों में नहीं, मैं समझता हूं कि वे क्या हैं, और उनका उपयोग क्यों किया जाता है, और वे कैसे उपयोगी हो सकते हैं, हालांकि मैं यह नहीं समझ सकता कि कैसे वृद्धि करने वाले पॉइंटर्स उपयोगी होंगे, क्या कोई इस बात का स्पष्टीकरण प्रदान कर सकता है कि पॉइंटर कैसे बढ़ाना है उपयोगी अवधारणा और मुहावरेदार सी ++?

यह प्रश्न तब आया जब मैंने ब्रेज़न स्ट्रॉस्ट्रुप की पुस्तक ए टूर ऑफ़ सी ++ पढ़ना शुरू किया, मुझे इस पुस्तक की सिफारिश की गई थी, क्योंकि मैं जावा से काफी परिचित हूं, और रेड्डिट के लोगों ने मुझे बताया कि यह एक अच्छी 'स्विचओवर' पुस्तक होगी ।


11
एक सूचक सिर्फ एक पुनरावृत्ति है
चार्ल्स साल्विया

1
यह कंप्यूटर वायरस लिखने के लिए पसंदीदा टूल में से एक है जो वे पढ़ते हैं जो उन्हें नहीं पढ़ना चाहिए। यह ऐप्स में भेद्यता के सबसे आम मामलों में से एक है (जब कोई पॉइंटर उस क्षेत्र के पिछले हिस्से को बढ़ाता है जहां उन्हें माना जाता है, तो इसे पढ़ता है या इसे लिखता है)> हार्टबेल्ड बग देखें।
सैम

1
@ मुख्य बिंदु के बारे में बुरा है।
क्रंचर

4
C ++ की अच्छी / बुरी बात यह है कि यह आपको segfault को कॉल करने से पहले बहुत कुछ करने की अनुमति देता है। आमतौर पर आपको किसी अन्य प्रक्रिया की मेमोरी, सिस्टम मेमोरी या संरक्षित ऐप मेमोरी तक पहुंचने की कोशिश करते समय सिगफॉल्ट मिलता है। सामान्य एप्लिकेशन पृष्ठों के अंदर किसी भी एक्सेस को सिस्टम द्वारा अनुमति दी जाती है, और यह उचित सीमा लागू करने के लिए प्रोग्रामर / कंपाइलर / भाषा के लिए नीचे है। C ++ आपको बहुत कुछ करने की अनुमति देता है जो आप चाहते हैं। Opensl के लिए अपने मेमोरी मैनेजर के रूप में - यह सच नहीं है। इसमें बस डिफ़ॉल्ट C ++ मेमोरी एक्सेस मैकेनिज्म है।
सैम

1
@INdek: यदि आप जिस मेमोरी को एक्सेस करने का प्रयास कर रहे हैं वह सुरक्षित है तो आपको केवल एक segfault मिलेगा। अधिकांश ऑपरेटिंग सिस्टम पृष्ठ स्तर पर सुरक्षा प्रदान करते हैं, इसलिए आप आमतौर पर उस पृष्ठ पर कुछ भी एक्सेस कर सकते हैं जिस पर आपका पॉइंटर शुरू होता है। यदि OS 4K पृष्ठ आकार का उपयोग करता है, तो यह उचित मात्रा में डेटा है। यदि आपका सूचक ढेर में कहीं बाहर शुरू होता है, तो यह किसी का अनुमान है कि आप कितना डेटा एक्सेस कर सकते हैं।
टीएमएन

जवाबों:


46

जब आपके पास एक सरणी होती है, तो आप सरणी के एक तत्व को इंगित करने के लिए एक पॉइंटर सेट कर सकते हैं:

int a[10];
int *p = &a[0];

यहाँ pपहले तत्व की ओर इशारा करता है a, जो है a[0]। अब आप पॉइंटर को अगले तत्व की ओर इशारा कर सकते हैं:

p++;

अब pदूसरे तत्व की ओर इशारा करता है a[1],। आप यहाँ का उपयोग कर तत्व तक पहुँच सकते हैं*p । यह जावा से अलग है जहां आपको किसी सरणी के तत्वों तक पहुंचने के लिए पूर्णांक इंडेक्स चर का उपयोग करना होगा।

C ++ में एक पॉइंटर को बढ़ाना जहां एक पॉइंटर के एक तत्व को इंगित नहीं करता है अपरिभाषित व्यवहार है


23
हां, C ++ के साथ आप प्रोग्रामिंग त्रुटियों से बचने के लिए जिम्मेदार हैं जैसे कि किसी सरणी के सीमा के बाहर पहुंच।
ग्रेग हेविगिल

9
नहीं, एक सूचक को बढ़ाना जो एक सरणी तत्व को छोड़कर किसी भी चीज़ की ओर इशारा करता है अपरिभाषित व्यवहार। हालांकि, यदि आप कुछ निम्न स्तर का कर रहे हैं और पोर्टेबल नहीं हैं, तो एक पॉइंटर को बढ़ाना आमतौर पर मेमोरी में अगली चीज तक पहुंचने से ज्यादा कुछ नहीं है, जो कुछ भी हो सकता है।
ग्रेग हेविगिल

4
वहाँ कुछ चीजें हैं जो हैं, या एक सरणी के रूप में इलाज किया जा सकता है; पाठ की एक स्ट्रिंग, वास्तव में, वर्णों की एक सरणी है। कुछ मामलों में, एक लंबे इंट को बाइट्स की एक सरणी के रूप में माना जाता है, हालांकि यह आपको आसानी से परेशानी में डाल सकता है।
AMADANON Inc.

6
यह आपको प्रकार बताता है , लेकिन व्यवहार 5.7 योजक संचालकों [expr.add] में वर्णित है। विशेष रूप से, 5.7 / 5 का कहना है कि एक-अतीत के अंत को छोड़कर सरणी के बाहर कहीं भी जाना यूबी है।
बेकार

4
अंतिम पैराग्राफ है: यदि पॉइंटर ऑपरेटर और परिणाम दोनों एक ही सरणी ऑब्जेक्ट के तत्वों को इंगित करते हैं, तो मूल्यांकन एक अतिप्रवाह पैदा नहीं करेगा; अन्यथा व्यवहार अपरिभाषित है । इसलिए, यदि परिणाम न तो सरणी में है और न ही पिछले-अंत में है, तो आप यूबी प्राप्त करते हैं।
बेकार

37

बढ़ते संकेत मुहावरेदार सी ++ हैं, क्योंकि सूचक शब्दार्थ सी ++ मानक पुस्तकालय (अलेक्जेंडर स्टेपानोव के एसटीएल से दूर) के पीछे डिजाइन दर्शन के एक मूलभूत पहलू को दर्शाते हैं ) के

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

बेशक, संकेत को बढ़ाने (या जोड़ने / घटाने) की क्षमता सी पर वापस जाती है। सी-स्ट्रिंग हेरफेर एल्गोरिदम के बहुत से बस सूचक अंकगणितीय का उपयोग करके लिखा जा सकता है। निम्नलिखित कोड पर विचार करें:

char string1[4] = "abc";
char string2[4];
char* src = string1;
char* dest = string2;
while ((*dest++ = *src++));

यह कोड एक शून्य-समाप्त सी-स्ट्रिंग की प्रतिलिपि बनाने के लिए सूचक अंकगणितीय का उपयोग करता है। लूप स्वचालित रूप से समाप्त हो जाता है जब यह नल का सामना करता है।

C ++ के साथ, सूचक शब्दार्थकों को पुनरावृत्तियों की अवधारणा के लिए सामान्यीकृत किया जाता है । अधिकांश मानक C ++ कंटेनर पुनरावृत्तियों प्रदान करते हैं, जिन्हें beginऔर endसदस्य कार्यों के माध्यम से पहुँचा जा सकता है । इटरेटर्स पॉइंटर्स की तरह व्यवहार करते हैं, इसमें उन्हें इंक्रीमेंट, डीरेफरेंस और कभी-कभी डीक्रिएट या एडवांस्ड किया जा सकता है।

इस पर पुनरावृति करने के लिए std::string, हम कहेंगे:

std::string s = "abcdef";
std::string::iterator it = s.begin();
for (; it != s.end(); ++it) std::cout << *it;

हम पुनरावृत्ति को वैसे ही बढ़ाते हैं जैसे हम एक साधारण सी-स्ट्रिंग के लिए एक संकेतक बढ़ाते हैं। इस अवधारणा के शक्तिशाली होने का कारण यह है कि आप ऐसे कार्यों को लिखने के लिए टेम्प्लेट का उपयोग कर सकते हैं जो किसी भी प्रकार के इटेटर के लिए काम करेंगे जो आवश्यक अवधारणा आवश्यकताओं को पूरा करता है। और यह एसटीएल की शक्ति है:

std::string s1 = "abcdef";
std::vector<char> buf;
std::copy(s1.begin(), s1.end(), std::back_inserter(buf));

यह कोड एक स्ट्रिंग को वेक्टर में कॉपी करता है। copyसमारोह है कि के साथ काम करेंगे एक टेम्पलेट है किसी भी (जो सादे संकेत भी शामिल है) इटरेटर कि समर्थन करता है incrementing। हम copyसादे C- स्ट्रिंग पर समान फ़ंक्शन का उपयोग कर सकते हैं :

   const char* s1 = "abcdef";
   std::vector<char> buf;
   std::copy(s1, s1 + std::strlen(s1), std::back_inserter(buf));

हम copyएक std::mapया एक std::setया किसी पर उपयोग कर सकते हैं कस्टम कंटेनर जो पुनरावृत्तियों का समर्थन करते हैं।

ध्यान दें कि पॉइंटर्स एक विशिष्ट प्रकार के पुनरावृत्ति हैं: रैंडम एक्सेस इटरेटर , जिसका अर्थ है कि वे ऑपरेटर +और -ऑपरेटर के साथ वेतन वृद्धि, गिरावट, और आगे बढ़ने का समर्थन करते हैं । अन्य पुनरावृत्त प्रकार केवल सूचक सिमेंटिक्स के एक उपसमूह का समर्थन करते हैं: एक द्विदिश पुनरावृत्ति कम से कम वेतन वृद्धि और क्षय का समर्थन करता है; एक आगे चलने वाला कम से कम वेतन वृद्धि का समर्थन करता है। (सभी पुनरावृत्त प्रकार dereferencing का समर्थन करते हैं।) copyफ़ंक्शन को एक पुनरावृत्ति की आवश्यकता होती है जो कम से कम वेतन वृद्धि का समर्थन करता है।

आप विभिन्न पुनरावृत्त अवधारणाओं के बारे में यहां पढ़ सकते हैं ।

तो, इंक्रीमेंटिंग पॉइंटर्स C-array पर C-array, या एक्सेस एलिमेंट्स / ऑफसेट्स को इटरेट करने का एक आइडियल सी ++ तरीका है।


3
हालांकि मैं पहले उदाहरण में पॉइंटर्स का उपयोग करता हूं, मैंने कभी भी इसके बारे में एक पुनरावृत्त के रूप में नहीं सोचा था, यह अब बहुत मायने रखता है।
dyesdyes 8

1
"लूप स्वचालित रूप से समाप्त हो जाता है जब यह नल का सामना करता है।" यह एक भयानक मुहावरा है।
चार्ल्स वुड

9
@CharlesWood, फिर मुझे लगता है कि आपको C बहुत भयानक
लगना

7
@CharlesWood: लूप कंट्रोल वेरिएबल के रूप में स्ट्रिंग की लंबाई का उपयोग करने का विकल्प है, जिसका अर्थ है स्ट्रिंग को दो बार ट्रेस करना (एक बार लंबाई निर्धारित करने के लिए, और एक बार वर्णों को कॉपी करने के लिए)। जब आप 1MHz PDP-7 पर चल रहे हैं, तो यह वास्तव में जोड़ना शुरू कर सकता है।
टीएमएन 16

3
@INdek: सबसे पहले, C और C ++ ब्रेकिंग परिवर्तन को लागू करने के लिए हर कीमत पर बचने की कोशिश करते हैं - और मैं कहूंगा कि स्ट्रिंग लीटर के डिफ़ॉल्ट व्यवहार को बदलना काफी संशोधन होगा। लेकिन सबसे महत्वपूर्ण बात यह है कि शून्य-टर्मिनेटेड स्ट्रिंग्स सिर्फ एक कन्वेंशन है (इस तथ्य का पालन करना आसान है कि स्ट्रिंग शाब्दिक रूप से डिफ़ॉल्ट रूप से शून्य-टर्मिनेटेड हैं और लाइब्रेरी फ़ंक्शंस उनसे उम्मीद करते हैं), कोई भी आपको सी में गिने स्ट्रिंग्स का उपयोग करने से नहीं रोकता है - वास्तव में, कई सी लाइब्रेरीज़ उनका उपयोग करती हैं (उदाहरण के लिए OLE के BSTR)।
मैटियो इटालिया

16

पॉइंटर अंकगणित C ++ में है क्योंकि यह C. में था। पॉइंटर अंकगणित C में है क्योंकि यह असेंबलर में एक सामान्य मुहावरा है।

बहुत सारे सिस्टम हैं जहां "वृद्धि रजिस्टर" "लोड निरंतर मान 1 और रजिस्टर में जोड़ें" की तुलना में तेज है। इसके अलावा, काफी कुछ सिस्टम आपको रजिस्टर बी में निर्दिष्ट पते से "DWORD को लोड" करने देते हैं, फिर एकल निर्देश में sizeof (DWORD) को B में जोड़ें। इन दिनों आप उम्मीद कर सकते हैं कि यह आपके लिए छाँटने के लिए एक अनुकूलन कंपाइलर हो, लेकिन 1973 में यह वास्तव में एक विकल्प नहीं था।

यह मूल रूप से एक ही कारण है कि सी सरणियों की सीमा-जाँच नहीं की जाती है और सी स्ट्रिंग में एक आकार नहीं होता है: भाषा एक ऐसी प्रणाली पर विकसित की गई थी जहाँ हर बाइट और हर निर्देश को गिना जाता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.