चर, असाइनमेंट और क्रमवार ट्रेस
यह प्रोग्राम क्या प्रिंट करेगा?
#include <stdio.h>
int main(void)
{
int x = 6;
int y = 4;
x = x + 3;
y = x - y;
printf("%d %d\n", x, y);
return 0;
}इन दो संख्याओं को जानने के लिए हमें मानों में होने वाले बदलाव क्रम से देखने होंगे। इस पाठ के अंत तक आप हर स्टेटमेंट के बाद प्रत्येक चर का मान बता सकेंगे और समझा सकेंगे कि वही परिणाम क्यों प्रिंट होता है।
जितना सिंटैक्स अभी चाहिए, उतना समझें
चर (variable) किसी मान को रखने की नाम वाली जगह है। यहाँ x और y में एक-एक int है: यानी 6, 0 या -3 जैसा पूर्णांक। int x = 6; में x की घोषणा होती है और उसका शुरुआती मान 6 रखा जाता है। इसे इनिशियलाइज़ेशन (initialization) कहते हैं। बाद का x = x + 3; उसी चर का मान बदलता है; वह दूसरा x घोषित नहीं करता।
यहाँ हर घोषणा या व्यंजक वाले स्टेटमेंट के अंत में सेमीकोलन है। ब्रेसिज़ के बीच main का मुख्य भाग है। इन पूरे प्रोग्रामों में int main(void) वह फ़ंक्शन है जहाँ से काम शुरू होता है; यहाँ void बताता है कि इसमें कोई पैरामीटर नहीं है। #include <stdio.h> से printf के उपयोग के लिए आवश्यक घोषणा मिलती है। इस include पंक्ति के अंत में सेमीकोलन नहीं आता। return 0; सफल निष्पादन समाप्त करता है। फ़ंक्शन विस्तार से सीखने से पहले भी आप ब्रेसिज़ के भीतर के स्टेटमेंट का ट्रेस कर सकते हैं। एक घोषणा में समान प्रकार के कई चर भी आ सकते हैं: int a=8, b=5; से a को 8 और b को 5 मिलता है। कॉमा उसी घोषणा में चरों के नाम और उनके शुरुआती मान अलग करता है।
printf("%d %d\n", x, y); दो पूर्णांक प्रिंट करता है। पहले %d की जगह x का मान और दूसरे की जगह y का मान आता है। उद्धरण-चिह्नों के भीतर का स्पेस संख्याओं के बीच छपता है और \n आउटपुट की पंक्ति समाप्त करता है। उद्धरण-चिह्न, %d और \n खुद नहीं छपते। इस कॉल में दोनों आर्ग्युमेंट केवल मौजूदा मान पढ़ते हैं; किसी चर को बदलते नहीं हैं। इसलिए C इम्प्लीमेंटेशन इन आर्ग्युमेंट के मान जिस क्रम में भी निकाले, इस परिणाम पर असर नहीं पड़ता।
केवल साधारण पूर्णांक गणना चाहिए: जैसे 17 - 9 का मान 8 और 6 * 4 का मान 24 है। पहले से प्रोग्रामिंग आना आवश्यक नहीं है। कोड में * गुणा का ऑपरेटर है; अक्षर x चर का नाम है, गुणा का चिह्न नहीं।
इस मॉड्यूल के कोड की सीमाएँ
पढ़ाने के लिए हम स्पष्ट रूप से ISO C11 के नियम लेते हैं। निश्चित परिणाम वाले हर उदाहरण में चरों को ज्ञात शुरुआती मान दिए गए हैं, सामान्य signed int लिया गया है और गणना के हर बीच के परिणाम को -32767 से 32767 के भीतर रखा गया है। जिस भाग या शेषफल की गणना वास्तव में होती है, उसका भाजक शून्य नहीं है। इससे उत्तर किसी खास मशीन के पूर्णांक आकार या निरूपण पर निर्भर नहीं करता। हम int को 32 बिट का मानकर नहीं चलते। इनपुट घोषणा में लिखे मान हैं; कीबोर्ड से इनपुट लेना आवश्यक नहीं है।
अवस्था: अभी कौन-से मान मौजूद हैं
अवस्था (state) किसी क्षण सभी चरों के वर्तमान मानों का समूह है। ट्रेस उन अवस्थाओं को क्रम से लिखने का तरीका है। हर स्टेटमेंट के बाद नया रिकॉर्ड लिखें। यदि स्टेटमेंट किसी चर को नया मान नहीं देता, तो उस चर का पिछला मान ही आगे लिखें।
x = x + 3; जैसे सरल असाइनमेंट (assignment) को पढ़ने का तरीका है:
- दाईं ओर के वर्तमान मान पढ़ें: यहाँ
xका पुराना मान। - दाईं ओर के व्यंजक का मान निकालें: पुराना
xऔर3जोड़ें। - परिणाम बाईं ओर लिखे चर में रखें: यहाँ
xमें। - अगले स्टेटमेंट के लिए इसी नई अवस्था का उपयोग करें।
दोनों जगह लिखे x की भूमिकाएँ अलग हैं: दाईं ओर वाला मान देता है और बाईं ओर वाला परिणाम रखने की जगह बताता है। = किसी मान को रखने का निर्देश है। यह ऐसा गणितीय समीकरण नहीं है जिसे हमेशा सही रहना हो। यदि अभी x का मान 6 है, तो x = x + 3; उसे ठीक तरह से 9 करता है; यह “6 बराबर 9” वाला असंभव समीकरण नहीं है। पढ़ने का यह तरीका यहाँ के सरल असाइनमेंट के लिए है, हर तरह के बदलाव करने वाले जटिल व्यंजक के लिए नहीं।
हल किया हुआ उदाहरण 1: वर्तमान अवस्था लें
शुरुआती प्रोग्राम पर लौटें। दोनों चरों के मान सामने रखें, उस चर का भी जिसका मान नहीं बदला।
int x = 6;के बाद:x=6; अभीyकी घोषणा नहीं हुई है।int y = 4;के बाद:x=6, y=4।x = x + 3;में6 + 3 = 9निकालें। स्टेटमेंट के बाद:x=9, y=4।y = x - y;में9 - 4 = 5निकालें। स्टेटमेंट के बाद:x=9, y=5।printfमेंx=9औरy=5पढ़े जाते हैं। आउटपुट9 5है, जिसके बाद नई पंक्ति आती है। चरों के मानx=9, y=5ही रहते हैं।
दूसरा असाइनमेंट अपडेट हुए x=9 को पढ़ता है। यदि फिर से शुरुआती x=6 लें, तो गलत y=2 मिलेगा। हर स्टेटमेंट पिछले स्टेटमेंट द्वारा छोड़ी गई अवस्था से शुरू होता है; ये असाइनमेंट एक साथ नहीं होते।
कॉपी का अर्थ स्थायी जुड़ाव नहीं है
किसी नए चर का शुरुआती मान पहले से मौजूद चर से लिया जा सकता है। नीचे copy को वही मान मिलता है जो उस समय source में है। वह source के साथ हमेशा बदलने वाला सूत्र नहीं बनता।
इस पाठ के सभी छोटे कोड ब्लॉक उसी main के भीतर रखे जाने वाले अलग-अलग उदाहरण हैं। ऊपर #include <stdio.h> और अंत में return 0; रहेगा। हर ब्लॉक नई शुरुआत वाला उदाहरण है; सभी ब्लॉक एक साथ न जोड़ें।
int source = 6;
int copy = source;
source = 10;
printf("%d %d\n", source, copy);घोषणाओं के बाद: source=6, copy=6। source = 10; के बाद: source=10, copy=6। आउटपुट 10 6 है। source को नया मान देने से copy में भी नया मान नहीं लिखा जाता। ट्रेस में रखी हुई संख्याएँ दर्ज होती हैं, लगातार कायम रहने वाले बीजगणितीय संबंध नहीं।
अंकगणितीय व्यंजक और समूह बनाना
व्यंजक (expression) बताता है कि कौन-सा मान निकालना है। इस पाठ के पूर्णांक व्यंजकों में:
+जोड़ता है,-घटाता है और*गुणा करता है/पूर्णांक भागफल देता है;%उसी भाग का शेषफल देता है- कोष्ठक किसी छोटे व्यंजक का समूह स्पष्ट करते हैं
*,/और%की प्राथमिकता+और-से अधिक है
प्राथमिकता तय करती है कि किस ऑपरेटर के साथ कौन-से ऑपरेंड जुड़ते हैं। यह ऑपरेंड का मान निकालने के समय-क्रम का सामान्य वादा नहीं है। हमारे अंकगणितीय उदाहरण व्यंजक के भीतर चर बदले बिना उनके मान पढ़ते हैं, इसलिए सही समूह से एक निश्चित परिणाम मिलता है।
int plain = 2 + 3 * 4;
int grouped = (2 + 3) * 4;
printf("%d %d\n", plain, grouped);plain का समूह 2 + (3 * 4) है: 3 * 4 से 12, फिर 2 + 12 से 14 मिलता है। grouped के कोष्ठक (2 + 3) से 5 लेने को कहते हैं, फिर 5 * 4 से 20 मिलता है। आउटपुट 14 20 है। C हर अंकगणितीय व्यंजक को बस सबसे बाएँ चिह्न से सबसे दाएँ चिह्न की ओर बढ़ते हुए नहीं निकालता।
पूरे समूह और बची वस्तुएँ
यदि 23 वस्तुओं के 5 वस्तुओं वाले समूह बनाएँ, तो चार पूरे समूहों में 20 वस्तुएँ लगेंगी और 3 बचेंगी। इसलिए 23 / 5 का मान 4 और 23 % 5 का मान 3 है। भागफल पूर्णांक है; वह 4.6 को निकटतम पूर्णांक में राउंड करके नहीं मिलता। जब कुल संख्या ऋणात्मक नहीं है और समूह का आकार धनात्मक है, तो शेषफल कम-से-कम शून्य और समूह के आकार से छोटा होगा। शेषफल शून्य होने पर भाग पूरा लगता है।
चिह्नों की भूमिकाएँ अलग रखें: दो पूर्णांक व्यंजकों के बीच % शेषफल है; printf के फ़ॉर्मैट में %d पूर्णांक प्रिंट करने का निर्देश है। इन उदाहरणों में इनमें से किसी का अर्थ प्रतिशत नहीं है। कभी शून्य से भाग न दें और शून्य भाजक के साथ शेषफल न निकालें। ऋणात्मक मान अगले पाठ में आएँगे; उन पर “शेषफल हमेशा धनात्मक होगा” वाला अनुमान न लगाएँ।
हल किया हुआ उदाहरण 2: कुल संख्या फिर बनाएँ
int total = 29;
int batch = 6;
int q = total / batch;
int r = total % batch;
int rebuilt = q * batch + r;
printf("%d %d %d\n", q, r, rebuilt);int total = 29;के बाद:total=29।int batch = 6;के बाद:total=29, batch=6।q = total / batchमें29 / 6लिया जाता है। चार पूरे समूह बनते हैं, क्योंकि4 * 6 = 24, जबकि5 * 6 = 30ज़्यादा है। अबq=4।r = total % batchउन चार समूहों के बाद बची मात्रा है:29 - 24 = 5। अबr=5;total,batchऔरqनहीं बदले।rebuilt = q * batch + rका समूह(q * batch) + rहै:4 * 6 + 5 = 29। अबrebuilt=29।- आर्ग्युमेंट के क्रम
q,r,rebuiltके अनुसार आउटपुट4 5 29है, जिसके बाद नई पंक्ति आती है।
जाँच q * batch + r = total दिखाती है कि दोनों परिणाम कैसे जुड़ते हैं। बताए गए भागफल और शेषफल को यह संबंध सही करना चाहिए और इन धनात्मक मानों के लिए शेष एक पूरे समूह से कम होना चाहिए। केवल ऐसी दो संख्याएँ चुनना जिनका जोड़ 29 हो, पर्याप्त नहीं है।
अभ्यास: हल देखने से पहले चारों प्रश्न करें
ये सीखने के लिए बनाए गए मौलिक प्रश्न हैं। इनके अंक नहीं हैं; उत्तर के साथ बीच की अवस्थाएँ भी लिखें।
अभ्यास प्रश्न 1: क्रम से दो असाइनमेंट
दोनों असाइनमेंट का ट्रेस करके a और b के अंतिम मान बताइए। कौन-सी पंक्ति प्रिंट होती है?
int a=8, b=5;
a=a-b;
b=b+a;
printf("%d %d\n",a,b);अभ्यास प्रश्न 2: भागफल और शेषफल
पूरे समूहों और बची वस्तुओं की संख्या निकालिए। प्रिंट होने वाली जोड़ी लिखिए और जाँचिए कि उससे कुल संख्या फिर बनती है।
int total=47, size=8;
printf("%d %d\n",total/size,total%size);अभ्यास प्रश्न 3: अंकगणित का समूह
C के समूह-नियमों से व्यंजक का मान निकालिए। अपने उत्तर को समझाने वाला समूह भी लिखिए।
int value=7+2*5-3;
printf("%d\n",value);अभ्यास प्रश्न 4: मान बदलने की कोशिश
क्या यह शुरुआती a और b के मान आपस में बदलता है? अंतिम जोड़ी बताइए और समझाइए कि a का मूल मान कहाँ जाता है।
int a=4,b=9;
a=b;
b=a;
printf("%d %d\n",a,b);अभ्यास के हल और गलत उत्तरों का कारण
अभ्यास प्रश्न 1 का हल
- शुरुआत में
a=8, b=5। a=a-bमें8-5=3मिलता है; अबa=3, b=5।b=b+aमें5+3=8मिलता है; अबa=3, b=8।- आउटपुट:
3 8, फिर नई पंक्ति।
यदि 3 13 मिला, तो अंतिम जोड़ में फिर से शुरुआती a=8 लिया गया। पहले असाइनमेंट के बाद उस पुरानी अवस्था को काट दें। यदि 8 13 मिला, तो पहले असाइनमेंट का परिणाम चर में रखा ही नहीं गया। दोनों स्टेटमेंट अपने बाईं ओर लिखे चर का मान बदलते हैं।
अभ्यास प्रश्न 2 का हल
8वाले पाँच पूरे समूहों में40वस्तुएँ लगती हैं; छठे के लिए48चाहिए, जो47से अधिक है।- भागफल
5है; शेषफल47-40=7है। - आउटपुट:
5 7, फिर नई पंक्ति। जाँच:5*8+7=47, साथ में0 <= 7और7 < 8।
6 -1 गलत है, क्योंकि पूर्णांक भाग में 47/8 को ऊपर राउंड नहीं किया जाता। 5 0 बची हुई वस्तुओं को छोड़ देता है। 5.875 0 में भिन्न वाला भागफल लिया गया है, जबकि यहाँ दोनों ऑपरेंड पूर्णांक हैं और / का परिणाम भी पूर्णांक है।
अभ्यास प्रश्न 3 का हल
- गुणा की प्राथमिकता अधिक है: समूह
7 + (2 * 5) - 3बनाएँ। - गुणनफल
10है। - बचे जोड़ और घटाव का समूह बाईं ओर से है:
(7 + 10) - 3 = 14। - आउटपुट:
14, फिर नई पंक्ति।
42 गलत समूह (7+2)*5-3 से मिलता है। 11 तब मिलेगा जब व्यंजक को बदलकर 7+2*(5-3) कर दें। प्रश्न में इनमें से कोई समूह नहीं दिया गया है। कोष्ठक लगाकर वास्तविक समूह दिखाएँ, नया व्यंजक न बनाएँ।
अभ्यास प्रश्न 4 का हल
- शुरुआत में
a=4, b=9। a=bसे9की कॉपीaमें जाती है; अबa=9, b=9। इस प्रोग्राम का कोई चर पुरानाa=4नहीं रखता।b=aवर्तमानa=9पढ़करbमें9रखता है; जोड़ी9, 9ही रहती है।- आउटपुट:
9 9, फिर नई पंक्ति। मान आपस में नहीं बदले।
9 4 मान लेता है कि दोनों मान एक साथ बदलते हैं। 4 9 मान लेता है कि किसी असाइनमेंट का असर नहीं हुआ। सरल सुधार यह है कि जिस पुराने मान के ऊपर नया मान लिखा जाएगा, उसे पहले बचा लें:
int a = 4;
int b = 9;
int saved = a;
a = b;
b = saved;
printf("%d %d\n", a, b);यहाँ a बदलने से पहले saved=4 हो जाता है। a=b के बाद a=9, b=9, saved=4। b=saved के बाद a=9, b=4, saved=4, और आउटपुट 9 4 है। सुधार इसलिए काम करता है क्योंकि पुरानी संख्या को साफ़ तौर पर अलग चर में रखा गया; C अपने-आप हर मिटा हुआ पुराना मान याद नहीं रखता।
आगे बढ़ने से पहले
अब आप वर्तमान अवस्था का ट्रेस कर सकते हैं, इनिशियलाइज़ेशन को बाद के असाइनमेंट से अलग पहचान सकते हैं और छोटे पूर्णांक व्यंजकों के समूह बना सकते हैं। अगले पाठ में हम व्यंजक से मिलने वाले मान और उससे चर में होने वाले बदलाव को अलग करेंगे, तथा ऋणात्मक पूर्णांकों के भाग को समझेंगे।
स्रोत संबंधी नोट
कोड और ट्रेस मौलिक हैं। भाषा का संदर्भ सार्वजनिक WG14 N1570 C11 समिति-ड्राफ्ट है: प्रवेश फ़ंक्शन §5.1.2.2.1p1; पूर्णांक की न्यूनतम सीमा §5.2.4.2.1; इनिशियलाइज़ेशन §6.7.9p11; सरल असाइनमेंट §6.5.16.1p2; समूह §6.5.5p1 और §6.5.6p1; पूर्णांक भाग/शेषफल §6.5.5p5–6; printf §7.21.6.3 और %d §7.21.6.1p8। ये नियमों के संदर्भ हैं; सीखना शुरू करने से पहले मानक दस्तावेज़ पढ़ना आवश्यक नहीं है।
उदाहरण से समझें
दो नाम लिखे मिटाए जा सकने वाले कार्ड सोचें। x वाले कार्ड पर 6 और y वाले पर 4 है। x = x + 3 का अर्थ है “6 पढ़ें, 9 निकालें, x वाले कार्ड का पुराना मान मिटाकर 9 लिखें।” इसके बाद y = x - y कार्डों के अभी के मान, यानी 9 और 4, पढ़ता है और y पर 5 लिखता है। दूसरे कार्ड पर संख्या की कॉपी करने से दोनों कार्ड आपस में बँध नहीं जाते। मिटाने के बाद भी पुरानी संख्या चाहिए तो पहले उसे तीसरे कार्ड पर रख लें। कार्ड रखे हुए मानों का उदाहरण हैं; इससे वास्तविक मेमोरी की बनावट के बारे में कोई दावा नहीं किया जा रहा।
सूत्र और नियम
- int x = 6; में x की घोषणा और इनिशियलाइज़ेशन है; x = 9; मौजूदा x को बदलता है
- अवस्था का अर्थ वर्तमान मान हैं। हर स्टेटमेंट के बाद उन्हें लिखें
- सरल असाइनमेंट में वर्तमान अवस्था से दाईं ओर का मान निकालें, फिर बाईं ओर के चर में रखें
- कॉपी किया गया मान स्रोत चर के साथ अपने-आप नहीं बदलता
- कोष्ठक समूह तय करते हैं; * / % की प्राथमिकता + - से अधिक है
- गैर-ऋणात्मक a और धनात्मक b के लिए: a / b पूरे समूह और a % b बची मात्रा है
- जाँच: (a / b) * b + a % b का मान a है; शेषफल b से छोटा है
- printf के हर %d के लिए एक int आर्ग्युमेंट रखें; \n पंक्ति समाप्त करता है
- C11 की सीमा: ज्ञात शुरुआती int मान, वास्तव में गणना होने पर भाजक शून्य नहीं, हर बीच का परिणाम -32767 से 32767 के भीतर
इस पाठ के नोट्स
प्रगति सहेजने के लिए साइन इन करें। साइन इन