सी, सिंटैक्स, पॉइंटर्स और फ़ंक्शंस के लिए सी सिंटैक्स को इस तरह डिज़ाइन क्यों किया गया था?


16

देखे जाने के बाद (और पूछा!) के समान कई सवाल

int (*f)(int (*a)[5])C में क्या मतलब है?

और यहां तक ​​कि यह देखते हुए कि उन्होंने लोगों को सी सिंटैक्स को समझने में मदद करने के लिए एक कार्यक्रम बनाया है , मैं मदद नहीं कर सकता लेकिन आश्चर्य है:

C का सिंटैक्स इस तरह से क्यों बनाया गया था?

उदाहरण के लिए, यदि मैं पॉइंटर्स डिज़ाइन कर रहा था, तो मैं "पॉइंटर को 10-एलीमेंट पॉइंट ऑफ़ पॉइंटर्स" में अनुवाद करूँगा

int*[10]* p;

और नहीं

int* (*p)[10];

जो मुझे लगता है कि ज्यादातर लोग सहमत होंगे बहुत कम सीधा है।

तो मैं सोच रहा हूँ, क्यों, उह, unintuitive वाक्यविन्यास? वहाँ एक विशेष समस्या थी वाक्यविन्यास हल करती है (शायद एक अस्पष्टता?) कि मैं अनजान हूँ?


2
आप जानते हैं कि इसका कोई वास्तविक उत्तर नहीं है, और ऐसे, प्रश्न। सही? आपको जो मिलेगा वह सिर्फ अनुमान है।
B:51овиЈ

7
@ वीजेओ - एक अच्छी तरह से "वास्तविक" (यानी। उद्देश्य) उत्तर हो सकता है - भाषा लेखकों और मानकों की समितियों ने इन फैसलों में से कई को स्पष्ट रूप से उचित (या कम से कम समझाया गया) है।
detly

मुझे नहीं लगता कि सी सिंटैक्स की तुलना में आपका प्रस्तावित सिंटैक्स आवश्यक रूप से "सहज" है। C यह क्या है; एक बार जब आप इसे सीख लेते हैं, तो आपके पास ये सवाल कभी नहीं होंगे। यदि आपने इसे नहीं सीखा है ... ठीक है, शायद यही असली समस्या है।
कालेब

1
@ कालेब: मजेदार यह कि आपने इतनी आसानी से कैसे निष्कर्ष निकाला, क्योंकि मैंने इसे सीखा है और मैंने अभी भी यह सवाल किया है ...
user541686

1
cdeclआदेश जटिल सी घोषणाओं डीकोड करने के लिए बहुत आसान है। Cdecl.org पर एक वेब इंटरफेस भी है ।
कीथ थॉम्पसन

जवाबों:


16

इसके इतिहास के बारे में मेरी समझ यह है कि यह दो मुख्य बिंदुओं पर आधारित है ...

सबसे पहले, भाषा लेखक टाइप-केंद्रित के बजाय सिंटैक्स को चर-केंद्रित बनाने के लिए पसंद करते थे। यही कारण है, वे घोषणा को देखने के लिए एक प्रोग्रामर चाहता था और लगता है कि "अगर मैं अभिव्यक्ति लिखना *func(arg), कि एक में परिणाम कर देंगे int, अगर मैं लिखने *arg[N]मैं एक नाव होगा" के बजाय " funcएक समारोह लेने के लिए एक सूचक होना चाहिए इस और लौट रहा है कि "।

विकिपीडिया पर सी प्रविष्टि का दावा है कि:

रिची का विचार संदर्भों में पहचानकर्ताओं को उनके उपयोग से मिलता जुलता घोषित करना था: "घोषणा उपयोग को दर्शाता है"।

... K & R2 के p122 का हवाला देते हुए, जो, मुझे, आपके लिए विस्तारित उद्धरण खोजने के लिए हाथ नहीं लगाना पड़ेगा।

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

याद रखें, सी की सफलता का हिस्सा इस तथ्य के कारण है कि कई अलग-अलग प्लेटफार्मों के लिए कंपाइलर लिखे गए थे, और इसलिए कम्पाइलर्स को लिखने के लिए आसान बनाने के लिए कुछ हद तक पठनीयता को अनदेखा करना बेहतर होगा।

यह कहने के बाद कि, मैं भाषा व्याकरण या संकलक लेखन का विशेषज्ञ नहीं हूँ। लेकिन मुझे पता है कि बहुत कुछ जानना बाकी है;)


2
"compilers लिखने के लिए आसान बनाने के" ... सी को छोड़कर जा रहा है के लिए कुख्यात है कठिन पार्स करने के लिए (केवल सी द्वारा शीर्ष पर रहा ++)।
Jan Hudec

1
@ जानहुडेक - अच्छा ... हाँ। यह एक वाटरटाइट स्टेटमेंट नहीं है। लेकिन जबकि सी को संदर्भ-मुक्त व्याकरण के रूप में पार्स करना असंभव है, एक बार एक व्यक्ति इसे पार्स करने का एक तरीका लेकर आया है, यह कठिन कदम है। और यह तथ्य यह है कि यह अपने शुरुआती दिनों में विपुल था , क्योंकि लोग आसानी से कंपाइलर्स को धमाका करने में सक्षम थे, इसलिए K & R ने कुछ संतुलन खो दिया होगा। (रिचर्ड गेब्रियल के कुख्यात द राइज़ ऑफ़ "
वर्सेज़ इज

मुझे इस पर सही होने की खुशी है, वैसे - मुझे यह सब पता नहीं है कि पार्सिंग और व्याकरण के बारे में बहुत कुछ है। मैं ऐतिहासिक तथ्य से निष्कर्ष पर जा रहा हूं।
8

12

सी भाषा की बहुत सी विषमताओं को कंप्यूटर द्वारा काम करने के तरीके से समझाया जा सकता है जब इसे डिजाइन किया गया था। बहुत सीमित मात्रा में भंडारण मेमोरी थी, इसलिए स्रोत कोड फ़ाइलों के आकार को स्वयं कम करना बहुत महत्वपूर्ण था । 70 और 80 के दशक में वापस प्रोग्रामिंग अभ्यास यह सुनिश्चित करने के लिए था कि स्रोत कोड संभव के रूप में कुछ वर्णों को समाहित करे, और अधिमानतः कोई अत्यधिक कोड कोड टिप्पणी नहीं।

यह निश्चित रूप से हास्यास्पद है, हार्ड ड्राइव पर बहुत अधिक असीमित भंडारण स्थान के साथ। लेकिन यह इस कारण का हिस्सा है कि सी में सामान्य रूप से ऐसे अजीब वाक्यविन्यास क्यों हैं।


विशेष रूप से सरणी पॉइंटर्स के बारे में, आपका दूसरा उदाहरण होना चाहिए int (*p)[10];(हाँ सिंटैक्स बहुत भ्रामक है)। मैं शायद यह पढ़ूंगा कि "इंट पॉइंटर टू आरेंज ऑफ टेन" ... जो कुछ हद तक समझ में आता है। यदि कोष्ठक के लिए नहीं है, तो संकलक इसके बजाय दस बिंदुओं की एक सरणी के रूप में व्याख्या करेगा, जो घोषणा को पूरी तरह से अर्थ देगा।

चूंकि एरे पॉइंट और फंक्शन पॉइंटर्स दोनों के सी में काफी अस्पष्ट सिंटैक्स है, इसलिए समझदारी वाली बात यह है कि अजीबता को दूर करना है। शायद इस तरह:

अस्पष्ट उदाहरण:

int func (int (*arr_ptr)[10])
{
  return 0;
}

int main()
{
  int array[10];
  int (*arr_ptr)[10]  = &array;
  int (*func_ptr)(int(*)[10]) = &func;

  func_ptr(arr_ptr);
}

गैर-अस्पष्ट, समकक्ष उदाहरण:

typedef int array_t[10];
typedef int (*funcptr_t)(array_t*);


int func (array_t* arr_ptr)
{
  return 0;
}

int main()
{
  int        array[10];
  array_t*   arr_ptr  = &array; /* non-obscure array pointer */
  funcptr_t  func_ptr = &func;  /* non-obscure function pointer */

  func_ptr(arr_ptr);
}

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


आह, अंत में एक उचित जवाब। :-) मैं उत्सुक हूँ कि कैसे विशेष वाक्यविन्यास वास्तव में स्रोत कोड आकार को छोटा कर देगा, लेकिन किसी भी तरह यह एक प्रशंसनीय विचार है और समझ में आता है। धन्यवाद। +1
user541686

मैं कहता हूं कि यह स्रोत कोड आकार के बारे में कम और संकलक लेखन के बारे में अधिक था, लेकिन निश्चित रूप से +1 "अजीबता दूर करना" के लिए। मेरे मानसिक स्वास्थ्य में नाटकीय रूप से सुधार हुआ जिस दिन मुझे एहसास हुआ कि मैं ऐसा कर सकता हूं।
detly

2
[उद्धरण वांछित] स्रोत कोड आकार की चीज़ पर। मैंने इस तरह की सीमा के बारे में कभी नहीं सुना है (हालांकि शायद यह "हर कोई जानता है")।
शॉन मैकमिलन

1
70 के दशक में आईबीएम, डीईसी और एक्सईआरओएक्स किट पर कोबोल, असेंबलर, कोरल और पीएल / 1 में मैंने 70 के दशक के कार्यक्रमों को कोड किया था और मैं कभी भी एक स्रोत कोड आकार सीमा में नहीं आया। सरणी आकार, निष्पादन योग्य आकार, कार्यक्रम का नाम आकार - लेकिन कभी स्रोत कोड आकार पर सीमाएं।
जेम्स एंडरसन

1
@ सीन मैकमिलन: मुझे नहीं लगता कि स्रोत कोड का आकार एक सीमा थी (विचार करें कि उस समय पास्कल जैसी मौखिक भाषाएं काफी लोकप्रिय थीं)। और यहां तक ​​कि अगर यह मामला था, तो मुझे लगता है कि स्रोत कोड को पूर्व-पार्स करना बहुत आसान होगा और लंबे कीवर्ड को एक-एक बाइट कोड द्वारा प्रतिस्थापित किया जाएगा (जैसे कुछ बेसिक दुभाषिए करते थे)। इसलिए मुझे लगता है कि तर्क "सी है क्योंकि यह एक ऐसी अवधि में आविष्कार किया गया था जब कम स्मृति उपलब्ध थी" थोड़ा कमजोर।
जियोर्जियो

7

यह बहुत आसान है: int *pइसका मतलब है कि *pएक इंट है; int a[5]इसका मतलब है कि a[i]एक इंट है।

int (*f)(int (*a)[5])

इसका मतलब है कि *fएक फ़ंक्शन है, *aपांच पूर्णांक का एक सरणी है, इसलिए fएक फ़ंक्शन एक सूचक को पांच पूर्णांक के सरणी में ले जा रहा है, और इंट वापस आ रहा है। हालाँकि, C में यह सूचक को किसी सरणी में पास करने के लिए उपयोगी नहीं है।

सी घोषणाएं बहुत कम ही इस जटिल हो पाती हैं।

इसके अलावा, आप टाइपडिफ का उपयोग करके स्पष्ट कर सकते हैं:

typedef int vec5[5];
int (*f)(vec5 *a);

4
माफी अगर यह अशिष्ट लगता है (मेरा मतलब यह नहीं है), लेकिन मुझे लगता है कि आप सवाल के पूरे बिंदु से चूक गए ...: \
user541686

2
@ मेहरदाद: मैं आपको बता नहीं सकता कि कर्निघन और रिची के दिमाग में क्या था; मैंने आपको वाक्य रचना के पीछे का तर्क बताया। मैं ज्यादातर लोगों के बारे में नहीं जानता, लेकिन मुझे नहीं लगता कि आपका सुझाव दिया वाक्यविन्यास स्पष्ट है।
केविन क्लाइन

मैं सहमत हूं - ऐसी जटिल घोषणा को देखना असामान्य है।
कालेब

सी के डिजाइन से पहले घोषणाओं typedef, const, volatile, और घोषणाओं के भीतर चीजों को प्रारंभ करने की क्षमता। घोषणा सिंटैक्स के कष्टप्रद अस्पष्टताओं में से कई (जैसे कि टाइप या घोषणा के लिए int const *p, *q;बाध्य होना चाहिए const) भाषा में मूल रूप से डिज़ाइन नहीं किया जा सकता है। काश भाषा ने टाइप और डिक्लेन्ड के बीच एक कोलोन जोड़ा होता, लेकिन क्वालिफायर के बिना अंतर्निहित "आरक्षित-शब्द" प्रकारों का उपयोग करते समय इसकी चूक की अनुमति दी। का अर्थ int: const *p,*q;और int const *: p,*q;स्पष्ट होगा।
सुपरकैट

3

मुझे लगता है कि आपको एक ऑपरेटर के रूप में * [] पर विचार करना होगा जो एक चर से जुड़े हैं। * एक चर से पहले लिखा जाता है, [] के बाद।

आइए पढ़ें प्रकार की अभिव्यक्ति

int* (*p)[10];

अंतरतम तत्व p है, इसलिए एक चर है

p

का अर्थ है: पी एक चर है।

चर से पहले एक * है, * ऑपरेटर को हमेशा अभिव्यक्ति से पहले रखा जाता है, इसलिए,

(*p)

का अर्थ है: चर पी एक सूचक है। बिना () के [] ऑपरेटर के दाईं ओर उच्च वरीयता होगी, अर्थात

**p[]

के रूप में पार्स किया जाएगा

*(*(p[]))

अगला कदम []: क्योंकि आगे नहीं है (), [] बाहरी * की तुलना में अधिक पूर्वता है, इसलिए

(*p)[]

का अर्थ है: (चर p एक सूचक है) एक सरणी में। फिर हमारे पास दूसरा है:

* (*p)[]

का अर्थ है: (चर एक संकेत के लिए चर) एक सूचक है

अंत में आपके पास अंतर ऑपरेटर (एक प्रकार का नाम) है, जिसमें सबसे कम पूर्वता है:

int* (*p)[]

का अर्थ है: (पूर्णांक की ओर एक सरणी के लिए) ((चर पी एक सूचक है)।

तो पूरी प्रणाली ऑपरेटरों के साथ प्रकार के भावों पर आधारित है, और प्रत्येक ऑपरेटर के अपने पूर्ववर्ती नियम हैं। यह बहुत जटिल प्रकारों को परिभाषित करने की अनुमति देता है।


0

यह इतना कठिन नहीं है जब आप सोचना शुरू करते हैं और सी कभी भी बहुत आसान भाषा नहीं थी। और int*[10]* pवास्तव में की तुलना में आसान नहीं है int* (*p)[10] और किस प्रकार का कश्मीर होगाint*[10]* p, k;


2
k एक असफल कोड की समीक्षा होगी, मैं काम कर सकता हूं कि कंपाइलर क्या करेगा, मैं भी परेशान हो सकता हूं, लेकिन मैं प्रोग्रामर का इरादा क्या है यह काम नहीं कर सकता - विफल ............
मैट्नज़

और k कोड की समीक्षा में विफल क्यों होगा?
Dainius

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

1
अनिवार्य रूप से एक ही पंक्ति पर अलग-अलग प्रकार के 3 (इस मामले में) चर घोषणाएं हैं जैसे int * p, int i [10] और int k। यह अस्वीकार्य है। एक ही प्रकार की कई घोषणाएँ स्वीकार्य हैं, बशर्ते कि चर का कोई न कोई संबंध हो जैसे कि अंतर चौड़ाई, ऊँचाई, गहराई; ध्यान रखें कि बहुत से लोग int * p का उपयोग करते हैं, इसलिए whats i में 'int * p, i;'।
मटनज़

1
@Mattnz जो कहना चाह रही है वह यह है कि आप जितना चाहें उतना होशियार हो सकते हैं, लेकिन यह सब तब व्यर्थ है जब आपका इरादा स्पष्ट नहीं होता है और / या आपका कोड खराब-लिखित / अपठनीय है। इस तरह के सामान से अक्सर टूटे हुए कोड और समय बर्बाद होता है। प्लस, pointer to intऔर intएक ही प्रकार के भी नहीं हैं, इसलिए उन्हें अलग से घोषित किया जाना चाहिए। अवधि। आदमी सुनो। वह एक कारण के लिए 18k प्रतिनिधि है।
ब्रैडेन बेस्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.