C में प्रोग्रामिंग
टोकन, पहचानकर्ता व नियंत्रण-प्रवाह
- C के टोकन कीवर्ड, पहचानकर्ता, अचर, स्ट्रिंग शाब्दिक, संकारक व विरामचिह्न हैं; पहचानकर्ता का कार्यक्षेत्र (ब्लॉक, फलन, फ़ाइल/वैश्विक) व भंडारण-वर्ग (auto, register, static, extern) स्वतंत्र गुण हैं — फलन के भीतर
staticकिसी स्थानीय चर की आयु को पूरे प्रोग्राम तक विस्तारित करता है जबकि उसका कार्यक्षेत्र स्थानीय ही छोड़ता है, जो इस उप-प्रकरण का मानक जाल है। - अनुक्रम नियंत्रण: if-else, switch (जो
breakन होने पर स्थितियों में गिरता चला जाता है — एक मानक जाल), while, do-while (मुख्य भाग कम-से-कम एक बार चलता है), for। उपप्रोग्राम नियंत्रण: सामान्य प्राचलों हेतु कॉल-बाय-वैल्यू शब्दार्थ वाले फलन (आमंत्रित को एक प्रति मिलती है; परिवर्तन वापस प्रसारित नहीं होते) — आमंत्रक के चर को प्रभावित करने का एकमात्र तरीका उसका पता स्पष्ट रूप से पास करना व आमंत्रित के भीतर संकेतक को डी-रेफ़रेंस करना है।
सरणी, स्ट्रक्ट, यूनियन व स्ट्रिंग
C सरणी का नाम, लगभग हर व्यंजक-संदर्भ में, अपने प्रथम अवयव के संकेतक में क्षय होता है — arr[i] को *(arr + i) के रूप में परिभाषित किया गया है, इसीलिए सरणी-सूचकांकन व संकेतक-अंकगणित समान परिणाम देते हैं, और sizeof(arr) (पूरी सरणी का बाइट-आकार) एक बार arr किसी फलन-प्राचल में क्षय हो जाने पर sizeof(ptr) से तीव्रता से भिन्न हो जाता है (वहाँ यह मात्र संकेतक का अपना आकार है)। स्ट्रक्ट नामित, स्वतंत्र रूप से संचित क्षेत्रों को समूहित करता है — उसका कुल आकार सामान्यतः उसके सदस्यों के आकारों के योग से कम-से-कम उतना है, और प्रायः अधिक, संकलक द्वारा प्रत्येक सदस्य की अपनी संरेखण-आवश्यकता हेतु डाली गई पैडिंग के कारण। C में स्ट्रिंग बिल्कुल भिन्न प्रकार नहीं है: यह '\0' (NUL) बाइट से समाप्त char सरणी है, अतः हर स्ट्रिंग-फलन (strlen, strcpy, strcat) वह समापक मिलने तक खोजता है, और उसके लिए स्थान छोड़ना भूल जाना मानक बफ़र-अतिप्रवाह का स्रोत है।
int arr[10] का sizeof(arr) == 40 है (4-बाइट int पर)। एक बार फलन-प्राचल के रूप में पास होने पर, void f(int arr[10]) को संकलक चुपचाप void f(int *arr) में पुनर्लिखित कर देता है, और f के भीतर sizeof(arr) संकेतक का अपना आकार देता है (64-बिट तंत्र पर सामान्यतः 8) — सरणी की मूल लंबाई पृथक रूप से पास किए बिना बस लुप्त हो जाती है।संकेतक व फलन
- संकेतक चर एक पता रखता है;
*pउसे डी-रेफ़रेंस करता है (इंगित मान तक पहुँचता है),&xx का पता लेता है। संकेतक-अंकगणित इंगित प्रकार के आकार से मापित होता है:int*परp + 1, 1 बाइट नहीं बल्किsizeof(int)बाइट आगे बढ़ता है — यही वह है जो संकेतकों से सरणी-सूचकांकन को अवयव-आकार से स्वतंत्र रूप से सही बनाता है। - फलन संकेतक किसी फलन के कोड का पता रखता है और उससे होकर बुलाया, सौंपा, या कॉलबैक के रूप में पास किया जा सकता है — qsort के तुलनक-आर्गुमेंट के पीछे की क्रियाविधि, व अधिक सामान्यतः, किसी भी C-शैली प्लग-इन प्रेषण-सारणी के पीछे की।
- गतिक स्मृति (
malloc/calloc/realloc/free) हीप पर आवंटित होती है और उसे आवंटित करने वाले फलन से अधिक जीवित रहती है — मानक बग हैं स्मृति-रिसाव (freeभूलना), लटकता संकेतक (मुक्त होने के बाद स्मृति का प्रयोग) व द्वि-मुक्ति (उसी खंड को दो बार मुक्त करना)।
फ़ाइल-प्रबंधन, कमांड-लाइन आर्गुमेंट व प्रीप्रोसेसर
- फ़ाइल-प्रबंधन:
fopenएकFILE*लौटाता है (विफलता पर NULL — प्रयोग से पहले सदा जाँचा जाता है);fread/fwriteद्विआधारी I/O करते हैं,fscanf/fprintfप्रारूपित पाठ-I/O;fcloseबफ़र किए निर्गम को फ्लश करता है व हैंडल मुक्त करता है, और इसे भूल जाना बफ़र में पड़ा वह आँकड़ा खो सकता है जो वास्तव में डिस्क पर कभी लिखा ही नहीं गया। - कमांड-लाइन आर्गुमेंट main के प्राचलों
int argc, char *argv[]में आते हैं — argc में प्रोग्राम का अपना नाम भी गिना जाता है (अतः argv[0] प्रोग्राम का अपना नाम है, और पहला वास्तविक उपयोगकर्ता-आर्गुमेंट argv[1] है)। - प्रीप्रोसेसर निर्देश (
#define,#include,#ifdef/#ifndef, प्राचल सहित मैक्रो) वास्तविक संकलन से पहले चलते हैं, शुद्ध पाठ्य-प्रतिस्थापन करते हुए — मैक्रो प्रकार-जाँचित नहीं होता और फलन की भाँति कार्यक्षेत्रित नहीं होता, इसीलिए कोष्ठक-युक्त मैक्रो#define SQ(x) ((x)*(x))को उन कोष्ठकों की आवश्यकता है ताकिSQ(a+b)जैसे बिना-कोष्ठक चुपचाप गलत विस्तार से बचा जा सके।
मुख्य बिंदु
- भंडारण-वर्ग (auto/static/extern/register) व कार्यक्षेत्र स्वतंत्र हैं — फलन के भीतर
staticकार्यक्षेत्र स्थानीय रखते हुए आयु पूरे प्रोग्राम तक विस्तारित करता है। - C कड़ाई से कॉल-बाय-वैल्यू है; आमंत्रक के चर को प्रभावित करने हेतु उसका पता पास करना व आमंत्रित के भीतर डी-रेफ़रेंस करना आवश्यक है।
- सरणी-प्राचल चुपचाप संकेतक बन जाता है — sizeof सरणी का वास्तविक आकार केवल उस फलन के भीतर देता है जहाँ वह घोषित है, प्राचल के रूप में पास होने के बाद कभी नहीं।
- C स्ट्रिंग NUL से समाप्त char सरणी है, भिन्न प्रकार नहीं; समापक हेतु स्थान भूलना मानक बफ़र-अतिप्रवाह का स्रोत है।
- प्रीप्रोसेसर संकलन से पहले शुद्ध पाठ्य-प्रतिस्थापन करता है — बिना-कोष्ठक मैक्रो-प्राचल चुपचाप गलत विस्तार दे सकता है।
अभ्यास प्रश्न (8)
उत्तर खोलने से पहले प्रत्येक प्रश्न हल करें। हर व्याख्या सही विकल्प के साथ लुभावना गलत विकल्प भी बताती है, क्योंकि अंक वहीं जाते हैं।
C फलन के भीतर
staticघोषित चर का यह गुण है:उत्तर देखें
उत्तर: A — स्थानीय कार्यक्षेत्र पर पूरे प्रोग्राम तक विस्तारित आयु
static आयु बदलता है (चर कॉलों के आर-पार अपना मान बनाए रखता है, प्रोग्राम की पूरी दौड़ हेतु एक बार आवंटित) कार्यक्षेत्र बदले बिना, जो उसी फलन तक सीमित रहता है जिसमें यह घोषित है।C में, किसी फलन से आमंत्रक के चर का मान बदलवाने हेतु, फलन को यह करना ही चाहिए:
उत्तर देखें
उत्तर: A — प्राचल के रूप में चर का पता (संकेतक) प्राप्त कर उसे डी-रेफ़रेंस करना
C कड़ाई से कॉल-बाय-वैल्यू है; सामान्य प्राचल एक प्रति है। चर का पता स्पष्ट रूप से पास करना व आमंत्रित के भीतर डी-रेफ़रेंस्ड संकेतक से होकर लिखना ही आमंत्रक के अपने भंडारण को प्रभावित करने का एकमात्र तरीका है।उसी फलन के भीतर घोषित व प्रयुक्त
int arr[10];हेतु,sizeof(arr)(4-बाइट int सहित) बराबर है:उत्तर देखें
उत्तर: A — 40
अपने ही घोषक फलन के भीतर, सरणी अभी संकेतक में क्षय नहीं हुई है, अतः sizeof सरणी का कुल बाइट-आकार देता है: 10 अवयव × 4 बाइट = 40। यह एक बार arr फलन-प्राचल के रूप में पास होने पर ही संकेतक के अपने आकार (सामान्यतः 8) में बदलता है।C में यूनियन, समान क्षेत्रों वाले स्ट्रक्ट की तुलना में:
उत्तर देखें
उत्तर: A — सभी क्षेत्रों को उसी स्मृति पर अध्यारोपित करता है, अपने सबसे बड़े सदस्य के आकार का
यूनियन का आकार उसके सबसे बड़े सदस्य के बराबर है, क्योंकि हर क्षेत्र वही भंडारण साझा करता है; स्ट्रक्ट का आकार कम-से-कम उसके सभी सदस्यों के आकारों के योग के बराबर है (साथ ही किसी संरेखण-पैडिंग सहित)।C में, स्ट्रिंग इससे समाप्त होती है:
उत्तर देखें
उत्तर: A — एक NUL बाइट ('\0')
लंबाई-उपसर्गित स्ट्रिंग निरूपण के विपरीत, C स्ट्रिंग को शुद्ध रूप से NUL बाइट पर समाप्त होती char सरणी के रूप में निरूपित करता है, और हर मानक स्ट्रिंग-फलन यह जानने हेतु उस बाइट को खोजता है कि स्ट्रिंग कहाँ समाप्त होती है।main(int argc, char *argv[])में, argv[0] रखता है:उत्तर देखें
उत्तर: A — प्रोग्राम का अपना नाम/आमंत्रण-पथ
argc में प्रोग्राम का अपना नाम भी आर्गुमेंटों में गिना जाता है, अतः argv[0] वही नाम/पथ है और पहला वास्तविक उपयोगकर्ता-प्रदत्त आर्गुमेंट argv[1] है — argv पर चलने वाले लूपों में एक आम ऑफ़-बाई-वन जाल।निम्नलिखित में कौन C में मानक गतिक-स्मृति बग हैं? (एक से अधिक विकल्प सही हो सकते हैं।)
उत्तर देखें
उत्तर: A — स्मृति-रिसाव — आवंटित स्मृति मुक्त करना भूलना; B — लटकता संकेतक — मुक्त हो चुकी स्मृति का प्रयोग; C — द्वि-मुक्ति — उसी खंड को दो बार मुक्त करना
स्मृति-रिसाव, लटकते संकेतक व द्वि-मुक्ति मैनुअल-स्मृति-प्रबंधन के तीन मानक बग हैं; स्थैतिक टाइपिंग C के प्रकार-तंत्र की एक असंबद्ध, सामान्य व सही विशेषता है, बग नहीं।किसी मैक्रो के पहले से परिभाषित न होने पर ही सशर्त रूप से कोड संकलित करने हेतु प्रयुक्त प्रीप्रोसेसर निर्देश #ifndef है, या वैकल्पिक रूप से #if !defined(...)। इन दो निर्देश-कीवर्डों में से छोटे का नाम बताइए (अग्रणी # के बिना)।
संख्यात्मक उत्तर — मान टाइप करें।
उत्तर देखें
उत्तर: ifndef
#ifndef("यदि परिभाषित नहीं") हैडर-गार्ड हेतु प्रयुक्त मानक निर्देश है, जो किसी हैडर की विषय-वस्तु को उसी अनुवाद-इकाई में एक से अधिक बार सम्मिलित होने से रोकता है।