C में तीर (->) ऑपरेटर क्यों मौजूद है?


264

डॉट ( .) ऑपरेटर का उपयोग किसी संरचना के सदस्य तक पहुंचने के लिए किया जाता है, जबकि ->C में तीर ऑपरेटर ( ) का उपयोग उस संरचना के सदस्य तक पहुंचने के लिए किया जाता है, जो कि प्रश्न में सूचक द्वारा संदर्भित किया जाता है।

सूचक के पास स्वयं कोई सदस्य नहीं है जिसे डॉट ऑपरेटर के साथ एक्सेस किया जा सकता है (यह वास्तव में वर्चुअल मेमोरी में किसी स्थान का वर्णन करने वाला केवल एक नंबर है, इसलिए इसका कोई सदस्य नहीं है)। इसलिए, अगर हम सिर्फ पॉइंटर को स्वचालित रूप से पॉइंटर को डिफाइन करने के लिए परिभाषित करते हैं तो कोई अस्पष्टता नहीं होगी यदि इसका इस्तेमाल पॉइंटर पर किया जाता है (एक सूचना जो कंपाइलर टाइम अफिक में कंपाइलर को पता है)।

तो क्यों भाषा के रचनाकारों ने इस उचित अनावश्यक ऑपरेटर को जोड़कर चीजों को और अधिक जटिल बनाने का फैसला किया है? बड़ा डिजाइन निर्णय क्या है?


1
संबंधित: stackoverflow.com/questions/221346/… - भी, आप ओवरराइड कर सकते हैं ->
18

16
@ क्रिस कि C ++ के बारे में जो निश्चित रूप से एक बड़ा अंतर बनाता है। लेकिन जब से हम इस बारे में बात कर रहे हैं कि C को इस तरह क्यों बनाया गया है, आइए हम दिखाते हैं कि हम 1970 के दशक में वापस आए - C ++ के अस्तित्व में आने से पहले।
रहस्यमयी नोवा

5
मेरा सबसे अच्छा अनुमान है, कि तीर ऑपरेटर नेत्रहीन व्यक्त करने के लिए मौजूद है "इसे देखो! आप यहां एक संकेतक के साथ काम कर रहे हैं"
क्रिस

4
एक नज़र में, मुझे लगता है कि यह सवाल बहुत अजीब है। सभी चीजें सोच-समझकर नहीं बनाई गई हैं। यदि आप इस शैली को अपने पूरे जीवन में रखते हैं, तो आपकी दुनिया सवालों से भरी होगी। अधिकांश मतों को जो जवाब मिला है वह वास्तव में जानकारीपूर्ण और स्पष्ट है। लेकिन यह आपके प्रश्न के मुख्य बिंदु पर नहीं आता है। अपने प्रश्न की शैली का पालन करें, मैं बहुत सारे प्रश्न पूछ सकता हूं। उदाहरण के लिए, कीवर्ड 'int' 'पूर्णांक' का संक्षिप्त नाम है; कीवर्ड 'डबल' भी छोटा क्यों नहीं है?
जून्वेंघ ११’१२ ६:५२

1
@junwanghe यह सवाल वास्तव में वैध चिंता का प्रतिनिधित्व करता है - .ऑपरेटर ऑपरेटर की तुलना में अधिक पूर्वता क्यों रखता है *? यदि ऐसा नहीं होता, तो हमारे पास * ptr.member और var.member हो सकता था।
मिलेनियमबग

जवाबों:


358

मैं आपके प्रश्न की व्याख्या दो प्रश्नों के रूप में करूँगा: 1) क्यों ->मौजूद है, और 2) क्यों .स्वचालित रूप से सूचक को निष्क्रिय नहीं करता है। दोनों सवालों के जवाब में ऐतिहासिक जड़ें हैं।

क्यों ->मौजूद है?

सी भाषा के पहले संस्करणों में से एक में (जिसे मैं " सी संदर्भ मैनुअल " के लिए सीआरएम के रूप में संदर्भित करूंगा , जो मई 1975 में 6 वें संस्करण यूनिक्स के साथ आया ->था ), ऑपरेटर का बहुत विशिष्ट अर्थ था, संयोजन *और .संयोजन का पर्याय नहीं

सीआरएम द्वारा वर्णित सी भाषा कई मायनों में आधुनिक सी से बहुत अलग थी। CRM संरचना में सदस्यों ने बाइट ऑफ़सेट की वैश्विक अवधारणा को लागू किया , जिसे बिना किसी प्रकार के प्रतिबंध के किसी भी पते के मूल्य में जोड़ा जा सकता है। यानी सभी संरचना सदस्यों के सभी नामों का स्वतंत्र वैश्विक अर्थ था (और, इसलिए, अद्वितीय होना चाहिए)। उदाहरण के लिए आप घोषित कर सकते हैं

struct S {
  int a;
  int b;
};

और नाम aऑफसेट 0 के लिए खड़ा होगा, जबकि नाम bऑफसेट 2 के लिए खड़ा होगा ( intआकार 2 का प्रकार और कोई पैडिंग नहीं)। भाषा को अनुवाद इकाई में सभी संरचनाओं के सभी सदस्यों की आवश्यकता है या तो अद्वितीय नाम हैं या समान ऑफसेट मूल्य के लिए खड़े हैं। उदाहरण के लिए एक ही अनुवाद इकाई में आप अतिरिक्त घोषणा कर सकते हैं

struct X {
  int a;
  int x;
};

और यह ठीक होगा, क्योंकि नाम aलगातार ऑफसेट 0. के लिए खड़ा होगा , लेकिन यह अतिरिक्त घोषणा

struct Y {
  int b;
  int a;
};

औपचारिक रूप से अमान्य होगा, क्योंकि इसने aऑफसेट 2 के bरूप में और ऑफसेट 0 के रूप में "पुनर्परिभाषित" करने का प्रयास किया था ।

और यह वह जगह है जहां ->ऑपरेटर आता है। चूंकि प्रत्येक संरचना सदस्य नाम का अपना आत्मनिर्भर वैश्विक अर्थ था, इसलिए भाषा ने इन जैसे भावों का समर्थन किया

int i = 5;
i->b = 42;  /* Write 42 into `int` at address 7 */
100->a = 0; /* Write 0 into `int` at address 100 */

पहले असाइनमेंट को कंपाइलर द्वारा "टेक एड्रेस 5, ऐड ओन इट्स 2टू ऐड " और परिणामी पते पर मान असाइन 42किया गया था int। यानी ऊपर आवंटित होगा 42करने के लिए intपते पर मूल्य 7। ध्यान दें कि इस प्रयोग ->ने बायीं ओर के अभिव्यक्ति के प्रकार की परवाह नहीं की। बाएं हाथ की ओर की व्याख्या एक सांख्यिक संख्यात्मक पते के रूप में की गई (जैसा कि यह एक सूचक या पूर्णांक है)।

इस तरह की प्रवंचना *और .संयोजन संभव नहीं था । आप नहीं कर सके

(*i).b = 42;

चूंकि *iपहले से ही एक अमान्य अभिव्यक्ति है। *ऑपरेटर, क्योंकि यह से अलग है .इसके संकार्य पर, लगाता अधिक सख्त प्रकार की आवश्यकताओं। इस सीमा के आसपास काम करने की क्षमता प्रदान करने के लिए सीआरएम ने ->ऑपरेटर को पेश किया , जो बाएं हाथ के ऑपरेंड के प्रकार से स्वतंत्र है।

जैसा कि कीथ ने टिप्पणियों में उल्लेख किया है, यह ->और *+ .संयोजन के बीच का अंतर है जिसे सीआरएम 7.1.8 में "आवश्यकता की छूट" के रूप में संदर्भित कर रहा है: आवश्यकता के विश्राम को छोड़कर जो E1सूचक प्रकार का है, अभिव्यक्ति E1−>MOSबिल्कुल इसके बराबर है(*E1).MOS

बाद में, के एंड आर सी में कई विशेषताओं को मूल रूप से सीआरएम में वर्णित किया गया था। "संरचनात्मक सदस्य के रूप में वैश्विक ऑफसेट पहचानकर्ता" का विचार पूरी तरह से हटा दिया गया था। और ->ऑपरेटर की कार्यक्षमता पूरी तरह से *और .संयोजन की कार्यक्षमता के समान हो गई ।

क्यों .सूचक को स्वचालित रूप से बाधित नहीं कर सकता है?

फिर से, भाषा के सीआरएम संस्करण में .ऑपरेटर के बाएं ऑपरेटर को एक अंतराल होना आवश्यक था । यह केवल उस ऑपरेंड पर लगाई गई आवश्यकता थी (और ->जैसा कि ऊपर बताया गया है , उससे अलग है )। ध्यान दें कि CRM को एक संरचनात्मक प्रकार के लिए बाएं ऑपरेंड की आवश्यकता नहीं थी .। यह सिर्फ एक आवेग होना चाहिए, कोई लवल्यू । इसका मतलब है कि सी के सीआरएम संस्करण में आप इस तरह कोड लिख सकते हैं

struct S { int a, b; };
struct T { float x, y, z; };

struct T c;
c.b = 55;

इस मामले में संकलक निरंतर मेमोरी ब्लॉक में बाइट-ऑफसेट 2 पर तैनात मूल्य 55में लिखेगा , भले ही कोई भी नाम टाइप न हो।intcstruct Tb । कंपाइलर वास्तविक प्रकार की बिल्कुल भी परवाह नहीं करेगा c। सभी के बारे में परवाह है कि cयह एक प्रकार का फल था: कुछ प्रकार की लेखन योग्य स्मृति ब्लॉक।

अब ध्यान दें कि अगर आपने ऐसा किया है

S *s;
...
s.b = 42;

कोड को वैध माना जाएगा (चूंकि sयह भी एक लवल्यू है) और कंपाइलर केवल बाइट-ऑफसेट में ही सूचक मेंs डेटा लिखने का प्रयास करेगा । कहने की जरूरत नहीं है, इस तरह की चीजें आसानी से मेमोरी ओवररन हो सकती हैं, लेकिन भाषा ऐसे मामलों से खुद को चिंतित नहीं किया।

ओवरलोडिंग ऑपरेटर के बारे में प्रस्तावित विचार भाषा के उस संस्करण में Ie . पॉइंटर प्रकारों के लिए काम नहीं करेगा: ऑपरेटर का .पहले से ही बहुत विशिष्ट अर्थ था जब पॉइंटर्स के साथ (लैवल्यू पॉइंटर्स के साथ या किसी भी अंतराल के साथ)। यह बहुत ही अजीब कार्यक्षमता थी, इसमें कोई संदेह नहीं है। लेकिन यह उस समय था।

बेशक, यह अजीब कार्यक्षमता अतिभारित पेश करने के खिलाफ एक बहुत मजबूत कारण नहीं है . सी - के एंड आर सी के पुन: संस्करण में संकेत के लिए ऑपरेटर (जैसा कि आपने सुझाव दिया था) नहीं है, लेकिन ऐसा नहीं किया गया है। हो सकता है कि उस समय सी के सीआरएम संस्करण में कुछ विरासत कोड लिखा गया था जिसे समर्थन किया जाना था।

(1975 सी संदर्भ मैनुअल के लिए URL स्थिर नहीं हो सकता है। एक और प्रतिलिपि, संभवतः कुछ सूक्ष्म अंतरों के साथ, यहाँ है ।)


10
और उद्धृत सी संदर्भ नियमावली के खंड 7.1.8 में कहा गया है, "आवश्यकता की छूट को छोड़कर कि E1 पॉइंटर प्रकार का है, अभिव्यक्ति '' E1−> MOS '' '(* E1) .MOS के बराबर है। '। "
कीथ थॉम्पसन

1
*iपता 5 पर यह कुछ डिफ़ॉल्ट प्रकार (int?) का अंतराल क्यों नहीं था ? तब (* i) .b ने उसी तरह काम किया होगा।
random832

5
@ लियो: ठीक है, कुछ लोग उच्च स्तरीय असेंबलर के रूप में सी भाषा को फैंसी करते हैं। सी इतिहास के उस दौर में भाषा वास्तव में एक उच्च-स्तरीय असेंबलर थी।
एनटी

29
हुह। तो यह बताता है कि यूनिक्स (जैसे, struct stat) में कई संरचनाएं अपने क्षेत्र (जैसे, st_mode) का उपसर्ग क्यों करती हैं ।
icktoofay

5
@ perfectionm1ng: ऐसा लग रहा है कि अल्काटेल-ल्यूसेंट द्वारा bell-labs.com पर कब्जा कर लिया गया है और मूल पृष्ठ चले गए हैं। मैंने लिंक को किसी अन्य साइट पर अपडेट किया है, हालांकि मैं यह नहीं कह सकता कि वह कितने समय तक रहेगा। वैसे भी, "रिची सी संदर्भ मैनुअल" के लिए गुग्लिंग आमतौर पर दस्तावेज़ पाता है।
एनटी

46

ऐतिहासिक (अच्छे और पहले से बताए गए) कारणों से परे, ऑपरेटरों की पूर्ववर्ती स्थिति के साथ भी थोड़ी समस्या है: डॉट ऑपरेटर की स्टार ऑपरेटर की तुलना में उच्च प्राथमिकता होती है, इसलिए यदि आपके पास पॉइंटर से लेकर पॉइंटर से लेकर स्ट्रक्चर तक ... इन दो समतुल्य हैं:

(*(*(*a).b).c).d

a->b->c->d

लेकिन दूसरा स्पष्ट रूप से अधिक पठनीय है। एरो ऑपरेटर के पास सर्वोच्च प्राथमिकता (बस डॉट के रूप में) है और सहयोगी दाएं से बाएं हैं। मुझे लगता है कि यह बिंदु संचालक से संरचना और संरचना दोनों के लिए डॉट ऑपरेटर के उपयोग की तुलना में स्पष्ट है, क्योंकि हम अभिव्यक्ति से प्रकार को जानते हैं बिना घोषणा को देखने के, यह दूसरी फ़ाइल में भी हो सकता है।


2
नेस्टेड डेटा प्रकारों के साथ दोनों स्ट्रक्चर्स और पॉइंटर्स टू स्ट्रक्चर्स यह चीजों को और अधिक कठिन बना सकते हैं, जैसा कि आपको प्रत्येक सबमम्बर-एक्सेस के लिए सही ऑपरेटर चुनने के बारे में सोचना होगा। आप शायद ab-> c-> d- या a-> bc-> d (i के साथ यह समस्या थी जब मैं freetype पुस्तकालय का उपयोग कर रहा था - मुझे यह देखने की जरूरत है कि यह हर समय स्रोत कोड है)। इसके अलावा यह नहीं समझाता है कि सूचक के साथ काम करते समय संकलक को सूचक को स्वचालित रूप से निष्क्रिय करने देना क्यों संभव नहीं होगा।
अकागा

3
जबकि आप जो तथ्य बता रहे हैं वे सही हैं, वे किसी भी तरह से मेरे मूल प्रश्न का उत्तर नहीं देते हैं। आप a-> और * (a) की समानता समझाते हैं। नोटेशन (जिसे पहले से ही अन्य प्रश्नों में कई बार समझाया जा चुका है) और साथ ही साथ भाषा के डिजाइन के बारे में अस्पष्ट बयान देने से कुछ हद तक अनियंत्रित हो जाता है। मुझे आपका उत्तर बहुत मददगार नहीं लगा, इसलिए पतन हुआ।
अकागा

16
@effeffe, ओपी कह रहा है कि भाषा को आसानी से व्याख्या की जा सकता था a.b.c.dके रूप में (*(*(*a).b).c).d, प्रतिपादन ->ऑपरेटर बेकार। तो ओपी का संस्करण ( a.b.c.d) समान रूप से पठनीय है (तुलना में a->b->c->d)। इसलिए आपका जवाब ओपी के सवाल का जवाब नहीं है।
शहबाज

4
@Shahbaz यह एक जावा प्रोग्रामर के लिए भी मामला हो, एक C / C ++ प्रोग्रामर समझ जाएगा a.b.c.dऔर a->b->c->dदो के रूप में बहुत अलग बातें: पहली बार एक नेस्टेड सब-वस्तु के लिए एक एकल स्मृति पहुँच है (वहाँ केवल इस मामले में एक भी स्मृति वस्तु है ), दूसरा तीन मेमोरी एक्सेस है, चार संभावित अलग-अलग वस्तुओं के माध्यम से पॉइंटर्स का पीछा करते हुए। यह मेमोरी लेआउट में बहुत बड़ा अंतर है, और मेरा मानना ​​है कि सी इन दोनों मामलों को बहुत ही स्पष्ट रूप से अलग करने में सही है।
सेंटास्टर - मोनिका

2
@ शहबाज मेरा मतलब यह नहीं था कि जावा प्रोग्रामर के अपमान के रूप में, वे बस पूरी तरह से निहित संकेत के साथ एक भाषा के लिए उपयोग किए जाते हैं। अगर मुझे एक जावा प्रोग्रामर के रूप में लाया गया था, तो मैं शायद उसी तरह से सोचूंगा ... वैसे भी, मुझे वास्तव में लगता है कि ऑपरेटर जो हम सी में देख रहे हैं, वह ओवरलोडिंग से कम है। हालांकि, मैं स्वीकार करता हूं कि हम सभी गणितज्ञों द्वारा खराब कर दिए गए हैं जो उदारतापूर्वक अपने ऑपरेटरों को बहुत अधिक हर चीज के लिए अधिभारित करते हैं। मैं उनकी प्रेरणा को भी समझता हूं, क्योंकि उपलब्ध प्रतीकों का सेट सीमित है। मुझे लगता है, अंत में यह सिर्फ सवाल है जहाँ आप रेखा
खींचते हैं

19

C कुछ भी अस्पष्ट नहीं बनाने पर भी अच्छा काम करता है।

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


4
यह सरल और सही उत्तर है। C ज्यादातर ओवरलोडिंग से बचने की कोशिश करता है जो IMO सी के बारे में सबसे अच्छी चीजों में से एक है
jforberg

10
C में बहुत सी चीजें अस्पष्ट और फजी हैं। इसमें निहित प्रकार के रूपांतरण हैं, गणित संचालक अतिभारित हैं, जंजीर अनुक्रमण कुछ अलग करता है, इस पर निर्भर करता है कि आप एक बहुआयामी सरणी या सूचक के एक सूचक को अनुक्रमित कर रहे हैं और कुछ भी स्थूल हो सकता है जिसमें कुछ भी छिपा हो (अपरकेस नामकरण सम्मेलन वहां मदद करता है लेकिन C doesn ' टी)।
PSkocik
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.