सी, पायथन, जावास्क्रिप्ट, गो, रस्ट, सॉलिडिटी जैसी भाषाओं और कंप्यूटर साइंस के बुनियादी और गणित के उन्नत सिद्धांतों को हिंदी में पढ़ने के लिए संपर्क करें। हम वेबसाइट और सॉफ्टवेयर भी बनाते हैं।
<!-- Beej's guide to C
# vim: ts=4:sw=4:nosi:et:tw=72
-->
हेलो, वर्ल्ड!
C से क्या उम्मीद रखें
"ये सीढ़ियाँ कहाँ जाती हैं?" "ऊपर जाती हैं।"
---Ray Stantz और Peter Venkman, Ghostbusters
C एक लो-लेवल भाषा है।
हमेशा से ऐसा नहीं था। उस ज़माने में, जब लोग ग्रेनाइट से पंच कार्ड तराशा करते थे, C [flw[असेंबली|Assembly_language]] जैसी निचले स्तर की भाषाओं की घिसाई-पिसाई से आज़ाद होने का ग़ज़ब का तरीक़ा थी।
लेकिन अब, इस आधुनिक दौर में, आज की पीढ़ी की भाषाएँ ऐसी-ऐसी सुविधाएँ देती हैं जो 1972 में, जब C बनी थी, थीं ही नहीं। यानी C काफ़ी बुनियादी भाषा है जिसमें ज़्यादा सुविधाएँ नहीं हैं। वह कुछ भी कर सकती है, पर उसके लिए आपसे मेहनत भी करवाती है।
तो फिर आज हम इसे इस्तेमाल ही क्यों करें?
सीखने के औज़ार के तौर पर: C न सिर्फ़ कंप्यूटिंग के इतिहास का एक आदरणीय टुकड़ा है, बल्कि यह [flw[बेयर मेटल|Bare_machine]] से उस तरह जुड़ी है जैसे आज की भाषाएँ नहीं जुड़ी होतीं। जब आप C सीखते हैं, तो आप जानते हैं कि सॉफ़्टवेयर निचले स्तर पर कंप्यूटर की मेमोरी से कैसे बात करता है। यहाँ कोई सीटबेल्ट नहीं है। आप ऐसा सॉफ़्टवेयर लिखेंगे जो क्रैश होगा, इसका मैं यक़ीन दिलाता हूँ। और यही सब मज़े का हिस्सा है!
काम के औज़ार के तौर पर: C आज भी कुछ ख़ास कामों में इस्तेमाल होती है, जैसे [flw[ऑपरेटिंग सिस्टम|Operating_system]] बनाना या [flw[एम्बेडेड सिस्टम|Embedded_system]] में। (हालाँकि [flw[Rust|Rust_(programming_language)]] प्रोग्रामिंग भाषा इन दोनों क्षेत्रों पर नज़र गड़ाए बैठी है!)
अगर आप किसी दूसरी भाषा से परिचित हैं, तो C की बहुत-सी बातें आसान लगेंगी। C ने कई दूसरी भाषाओं को प्रेरित किया है, और आपको उसके अंश Go, Rust, Swift, Python, JavaScript, Java और तरह-तरह की भाषाओं में दिखेंगे। वे हिस्से जाने-पहचाने लगेंगे।
C की जो एक चीज़ लोगों को अटका देती है, वह है पॉइंटर। बाक़ी लगभग सब कुछ जाना-पहचाना है, पर पॉइंटर अनोखे हैं। पॉइंटर के पीछे की अवधारणा शायद आप पहले से जानते हों, लेकिन C आपसे उसे साफ़-साफ़ ज़ाहिर करवाती है, और वह भी ऐसे ऑपरेटरों से जो आपने शायद पहले कभी देखे नहीं।
यह ख़ास तौर पर कपटी इसलिए है कि जैसे ही आप पॉइंटर को [flw[ग्रोक|Grok]] कर लेते हैं, वे अचानक आसान हो जाते हैं। लेकिन उस पल तक वे फिसलने वाली ईल मछलियों जैसे हैं।
C में बाक़ी सब कुछ बस किसी ऐसे काम का एक और तरीक़ा (या कभी-कभी वही तरीक़ा!) याद करना है जो आप पहले कर चुके हैं। पॉइंटर ही अजूबा हिस्सा हैं। और, कहा जाए तो, पॉइंटर भी उसी थीम की किस्में हैं जिससे आप शायद परिचित हैं।
तो तैयार हो जाइए एक रोमांचक सफ़र के लिए, जिसमें आप असेंबली के बिना कंप्यूटर के केंद्र के उतना क़रीब पहुँचेंगे जितना संभव है, और वह भी अब तक की सबसे प्रभावशाली कंप्यूटर भाषा^[मुझे पता है कोई न कोई इस पर मुझसे बहस करेगा, पर यह कम से कम शीर्ष तीन में तो होगी ही, है ना?] में। कसकर पकड़ लीजिए!
हेलो, वर्ल्ड!
[iHello, world]यह C प्रोग्राम का सबसे मानक उदाहरण है। हर कोई इसे इस्तेमाल करता है। (ध्यान दें कि बाईं ओर की संख्याएँ सिर्फ़ पाठक के संदर्भ के लिए हैं, वे सोर्स कोड का हिस्सा नहीं हैं।)
/* हेलो वर्ल्ड प्रोग्राम */
#include <stdio.h>
int main(void)
{
printf("Hello, World!\n"); // असली काम यहाँ होता है
}
हम लंबी बाँहों वाले मज़बूत रबर के दस्ताने पहनेंगे, एक स्कैल्पल उठाएँगे, और इस चीज़ को चीरकर देखेंगे कि यह चलती कैसे है। तो हाथ धो लीजिए, क्योंकि हम शुरू करते हैं। बहुत हौले से चीरते हुए...
[i[Comments]<]
पहले आसान चीज़ निपटा लें: /* और */ इन दो-अक्षरी चिह्नों के बीच की कोई भी चीज़ कमेंट है और कंपाइलर उसे पूरी तरह अनदेखा कर देगा। यही बात // के बाद उस पंक्ति में लिखी हर चीज़ पर भी लागू होती है। इससे आप ख़ुद को और दूसरों को संदेश छोड़ सकते हैं, ताकि आगे कभी दूर भविष्य में जब आप अपना कोड दोबारा पढ़ने आएँ, तो आपको पता रहे कि आख़िर आप करना क्या चाह रहे थे। मेरा यक़ीन कीजिए, आप भूल जाएँगे; ऐसा होता है।
[i[Comments]>]
[i[C Preprocessor]<]
[i[#include directive]<]
अब, यह #include क्या बला है? छी! ख़ैर, यह C प्रीप्रोसेसर से कहता है कि किसी दूसरी फ़ाइल की सामग्री उठाओ और ठीक वहीं कोड में डाल दो।
रुकिए---C प्रीप्रोसेसर क्या होता है? अच्छा सवाल है। कंपाइलेशन के दो चरण^[वैसे, तकनीकी तौर पर दो से ज़्यादा होते हैं, पर चलिए मान लेते हैं कि दो ही हैं---अज्ञान में ही आनंद है, है ना?] होते हैं: प्रीप्रोसेसर और कंपाइलर। [i[Octothorpe]]जो भी चीज़ पाउंड चिह्न, हैश चिह्न या "ऑक्टोथॉर्प" (#) से शुरू होती है, उस पर प्रीप्रोसेसर[i[Preprocessor]] काम करता है, कंपाइलर के शुरू होने से भी पहले। इन्हें प्रीप्रोसेसर डायरेक्टिव कहते हैं, और आम डायरेक्टिव #include और #define हैं।[i[#define directive]] इस पर आगे और बात होगी।[i[#include directive]>][i[C Preprocessor]>]
आगे बढ़ने से पहले, मैं यह बताने की ज़हमत ही क्यों उठा रहा हूँ कि पाउंड चिह्न को ऑक्टोथॉर्प कहते हैं? जवाब सीधा है: मुझे ऑक्टोथॉर्प शब्द इतना ज़बरदस्त मज़ेदार लगता है कि जब भी मौक़ा मिले, मैं बिना वजह इसका नाम फैलाए बिना नहीं रह सकता। ऑक्टोथॉर्प। ऑक्टोथॉर्प, ऑक्टोथॉर्प, ऑक्टोथॉर्प।
तो ख़ैर। C प्रीप्रोसेसर जब सब कुछ प्रीप्रोसेस कर चुकता है, तो नतीजे कंपाइलर के हाथ में जाने के लिए तैयार होते हैं, जो उनसे [flw[असेंबली कोड|Assembly_language]], [flw[मशीन कोड|Machine_code]], या जो भी करने वाला हो, वह बना देता है। मशीन कोड वह "भाषा" है जिसे CPU समझता है, और वह इसे बहुत तेज़ी से समझ लेता है। C प्रोग्रामों के तेज़ होने की एक वजह यही है।
फ़िलहाल कंपाइलेशन के तकनीकी ब्योरों की चिंता न करें; बस इतना जान लें कि आपका सोर्स पहले प्रीप्रोसेसर से गुज़रता है, फिर उसका आउटपुट कंपाइलर से गुज़रता है, और फिर उससे एक एक्ज़ीक्यूटेबल बनता है जिसे आप चला सकें।
पंक्ति के बाक़ी हिस्से का क्या? [i[stdio.h header file]<]<stdio.h> क्या है? इसे हेडर फ़ाइल कहते हैं। अंत का dot-h इसका भेद खोल देता है। असल में यह "स्टैंडर्ड I/O" (stdio) हेडर फ़ाइल है, जिसे आप आगे चलकर जानेंगे भी और चाहने भी लगेंगे। यह हमें I/O की ढेर सारी कार्यक्षमता^[तकनीकी रूप से, इसमें आम इनपुट और आउटपुट की ज़रूरतों के लिए प्रीप्रोसेसर डायरेक्टिव और फ़ंक्शन प्रोटोटाइप (इन पर आगे और बात होगी) होते हैं।] तक पहुँच देती है। हमारे डेमो प्रोग्राम में हम स्ट्रिंग "Hello, World!" आउटपुट कर रहे हैं, इसलिए इसके लिए हमें ख़ास तौर पर [i[printf() function]<] printf() फ़ंक्शन तक पहुँच चाहिए। <stdio.h> फ़ाइल हमें यही पहुँच देती है। मोटे तौर पर, अगर हम #include <stdio.h> के बिना printf() इस्तेमाल करने की कोशिश करते, तो कंपाइलर हमसे इसकी शिकायत करता।
मुझे कैसे पता चला कि printf() के लिए #include <stdio.h> चाहिए?[i[printf() function]>] जवाब: यह डॉक्यूमेंटेशन में लिखा है। अगर आप Unix सिस्टम पर हैं, तो man 3 printf चलाइए, और man पेज के ठीक ऊपर ही लिखा मिलेगा कि कौन-सी हेडर फ़ाइलें ज़रूरी हैं। या इस किताब का रेफ़रेंस वाला हिस्सा देखिए। :-) [i[stdio.h header file]>]
बाप रे! यह सब बस पहली पंक्ति समझाने में लग गया! लेकिन सच तो यह है कि उसकी पूरी चीर-फाड़ हो चुकी है। अब कोई रहस्य बाक़ी नहीं रहेगा!
तो ज़रा दम ले लीजिए...नमूना कोड पर एक बार फिर नज़र डालिए। बस दो-तीन आसान पंक्तियाँ बची हैं।
छुट्टी से वापसी पर स्वागत है! मुझे पता है आपने सच में ब्रेक नहीं लिया; मैं तो बस आपका दिल रख रहा था।
[i[main() function]<]अगली पंक्ति main() है। यह फ़ंक्शन main() की परिभाषा है; घुँघराले कोष्ठकों ({ और }) के बीच की हर चीज़ फ़ंक्शन की परिभाषा का हिस्सा है।
(वैसे, किसी दूसरे फ़ंक्शन को कॉल कैसे करते हैं? जवाब printf() वाली पंक्ति में है, पर उस पर हम एक मिनट में आएँगे।)
अब, main फ़ंक्शन कई मायनों में ख़ास है, पर एक बात सबसे ऊपर है: यह वह फ़ंक्शन है जो आपका प्रोग्राम चलना शुरू होते ही अपने-आप कॉल हो जाता है। main() से पहले आपका कुछ भी कॉल नहीं होता। हमारे उदाहरण में यह ठीक काम करता है, क्योंकि हमें बस एक पंक्ति प्रिंट करनी है और बाहर निकल जाना है।
अरे हाँ, एक बात और: जैसे ही प्रोग्राम main() के अंत से आगे निकलता है, यानी नीचे वहाँ बंद होने वाले घुँघराले कोष्ठक पर, प्रोग्राम बाहर निकल जाएगा, और आप अपने कमांड प्रॉम्प्ट पर लौट आएँगे।
तो अब हम जानते हैं कि उस प्रोग्राम ने एक हेडर फ़ाइल, stdio.h[i[stdio.h]T], अंदर ली है, और एक main() फ़ंक्शन घोषित किया है जो प्रोग्राम शुरू होने पर चलेगा। main() के अंदर कौन-सी मज़ेदार चीज़ें हैं?[i[main() function]>]
मुझे बड़ी ख़ुशी है कि आपने पूछा। सच में! हमारे पास बस एक ही मज़ेदार चीज़ है: फ़ंक्शन [i[printf() function]<]printf() का एक कॉल। आप कई तरीक़ों से पहचान सकते हैं कि यह फ़ंक्शन कॉल है, फ़ंक्शन की परिभाषा नहीं, पर एक संकेत यह है कि इसके बाद घुँघराले कोष्ठक नहीं हैं। और आप फ़ंक्शन कॉल को सेमीकोलन से ख़त्म करते हैं ताकि कंपाइलर जान ले कि यह एक्सप्रेशन का अंत है। आप लगभग हर चीज़ के बाद सेमीकोलन लगाते रहेंगे, जैसा कि आगे दिखेगा।
आप फ़ंक्शन printf()[i[printf() function]>] को एक आर्ग्युमेंट दे रहे हैं: एक स्ट्रिंग, जो कॉल करने पर प्रिंट होगी। अरे वाह---हम फ़ंक्शन कॉल कर रहे हैं! हम तो छा गए! रुकिए, रुकिए---अकड़िए मत। [i[\n newline]<]स्ट्रिंग के अंत में वह अजीब-सा \n क्या है? दरअसल स्ट्रिंग के ज़्यादातर अक्षर ठीक वैसे ही प्रिंट होते हैं जैसे संग्रहीत हैं। पर कुछ अक्षर ऐसे हैं जिन्हें स्क्रीन पर ठीक से छापा नहीं जा सकता, और उन्हें दो-अक्षरों वाले बैकस्लैश कोड के रूप में डाला जाता है। इनमें सबसे लोकप्रिय \n है ("बैकस्लैश-N" या सीधे "न्यूलाइन" पढ़ें), जो न्यूलाइन अक्षर के लिए है। यह वह अक्षर है जिसके कारण आगे की छपाई मौजूदा पंक्ति की जगह अगली पंक्ति की शुरुआत से होती है। यह वैसा ही है जैसे पंक्ति के अंत में रिटर्न दबाना।[i[\n newline]>]
तो उस कोड को hello.c नाम की फ़ाइल में कॉपी कीजिए और उसे बिल्ड कीजिए। Unix जैसे प्लेटफ़ॉर्म (जैसे Linux, BSD, Mac या WSL) पर कमांड लाइन से आप इस तरह की कमांड से बिल्ड करेंगे:
[i[gcc compiler]]
gcc -o hello hello.c
(इसका मतलब है "hello.c को कंपाइल करो, और hello नाम की एक एक्ज़ीक्यूटेबल बनाओ"।)
यह हो जाने के बाद आपके पास hello नाम की एक फ़ाइल होनी चाहिए जिसे आप इस कमांड से चला सकते हैं:
./hello
(शुरू का ./ शेल को बताता है कि "मौजूदा डायरेक्टरी से चलाओ"।)
और देखिए क्या होता है:
Hello, World!
हो गया और टेस्ट भी हो गया! रिलीज़ कर दो![i[Hello, world]>]
कंपाइलेशन के ब्योरे
[i[Compilation]<]चलिए इस पर थोड़ी और बात करें कि C प्रोग्राम कैसे बिल्ड किए जाते हैं, और पर्दे के पीछे वहाँ होता क्या है।
दूसरी भाषाओं की तरह C में भी सोर्स कोड होता है। लेकिन आप किस भाषा से आ रहे हैं, इस पर निर्भर करते हुए, हो सकता है आपको अपने सोर्स कोड को कभी एक्ज़ीक्यूटेबल में कंपाइल करना ही न पड़ा हो।
कंपाइलेशन वह प्रक्रिया है जिसमें आपका C सोर्स कोड लेकर उसे ऐसे प्रोग्राम में बदला जाता है जिसे आपका ऑपरेटिंग सिस्टम चला सके।
JavaScript और Python के डेवलपर अलग से कंपाइलेशन चरण के आदी बिल्कुल नहीं होते--हालाँकि पर्दे के पीछे वह हो रहा होता है! Python आपके सोर्स कोड को बाइटकोड नाम की चीज़ में कंपाइल करता है जिसे Python वर्चुअल मशीन चला सकती है। Java के डेवलपर कंपाइलेशन के आदी होते हैं, पर उससे Java वर्चुअल मशीन के लिए बाइटकोड बनता है।
C को कंपाइल करने पर मशीन कोड बनता है। यह वे 1 और 0 हैं जिन्हें CPU सीधे और तेज़ी से चला सकता है।
जिन भाषाओं को आम तौर पर कंपाइल नहीं किया जाता, उन्हें इंटरप्रेटेड
भाषाएँ कहते हैं। पर जैसा हमने Java और Python के साथ बताया, उनमें भी
कंपाइलेशन चरण होता है। और ऐसा कोई नियम नहीं कि C को इंटरप्रेट नहीं
किया जा सकता। (C इंटरप्रेटर मौजूद हैं!) संक्षेप में, यह धुंधले इलाक़ों
का एक पुलिंदा है। कंपाइलेशन आम तौर पर बस सोर्स कोड लेकर उसे किसी दूसरे,
ज़्यादा आसानी से चलने वाले रूप में बदलना है।
C कंपाइलर वह प्रोग्राम है जो कंपाइलेशन करता है।
जैसा हम पहले कह चुके हैं, gcc एक कंपाइलर है जो बहुत-से [flw[Unix जैसे ऑपरेटिंग सिस्टमों|Unix]] पर इंस्टॉल रहता है। और इसे आम तौर पर टर्मिनल में कमांड लाइन से चलाया जाता है, पर हमेशा नहीं। आप इसे अपने IDE से भी चला सकते हैं।
तो कमांड लाइन से बिल्ड कैसे करते हैं?
gcc से बिल्ड करना
[i[gcc compiler]<]अगर मौजूदा डायरेक्टरी में आपके पास hello.c नाम की सोर्स फ़ाइल है, तो आप टर्मिनल में यह कमांड टाइप करके उसे hello नाम के प्रोग्राम में बिल्ड कर सकते हैं:
gcc -o hello hello.c
-o का मतलब है "आउटपुट इस फ़ाइल में दो"^[अगर आप आउटपुट की फ़ाइल का नाम न दें, तो वह डिफ़ॉल्ट रूप से a.out नाम की फ़ाइल में बनेगा---इस फ़ाइल नाम की जड़ें Unix के इतिहास में बहुत गहरी हैं।]। और अंत में hello.c है, यानी उस फ़ाइल का नाम जिसे हम कंपाइल करना चाहते हैं।
अगर आपका सोर्स कई फ़ाइलों में बँटा है, तो आप सभी .c फ़ाइलों को कमांड लाइन में डालकर उन्हें एक साथ कंपाइल कर सकते हैं (लगभग ऐसे जैसे वे एक ही फ़ाइल हों, पर असल में नियम इससे ज़्यादा जटिल हैं):
[i[Integrated Development Environment]<]अगर आप इंटीग्रेटेड डेवलपमेंट एनवायरनमेंट (IDE) इस्तेमाल कर रहे हैं, तो शायद आपको कमांड लाइन से बिल्ड करने की ज़रूरत नहीं पड़ेगी।
Visual Studio में CTRL-F7 बिल्ड करेगा, और CTRL-F5 चलाएगा।
VS Code में आप डीबगर के ज़रिए चलाने के लिए F5 दबा सकते हैं। (इसके लिए आपको C/C++ एक्सटेंशन इंस्टॉल करना होगा।)
XCode में आप COMMAND-B से बिल्ड और COMMAND-R से रन कर सकते हैं। कमांड लाइन टूल्स पाने के लिए Google पर "XCode command line tools" खोजिए, आपको उन्हें इंस्टॉल करने के निर्देश मिल जाएँगे।
शुरुआत के लिए मैं आपको सलाह दूँगा कि कमांड लाइन से भी बिल्ड करने की कोशिश करें---वह तो इतिहास है![i[Integrated Development Environment]>]
C के संस्करण
[i[Language versions]<]C ने इन सालों में लंबा सफ़र तय किया है, और इसके कई नामित संस्करण रहे हैं जो बताते हैं कि आप भाषा की कौन-सी बोली इस्तेमाल कर रहे हैं।
ये आम तौर पर स्पेसिफ़िकेशन के साल की ओर इशारा करते हैं।
सबसे मशहूर C89, C99, C11 और C23 हैं। इस किताब में हम आख़िरी वाले पर ध्यान देंगे।
पर यह रही एक ज़्यादा पूरी तालिका:
संस्करण
विवरण
K&R C
1978, मूल संस्करण। इसका नाम Brian Kernighan और Dennis Ritchie के नाम पर पड़ा। Ritchie ने भाषा को डिज़ाइन किया और कोड किया, और Kernighan उस पर लिखी किताब के सह-लेखक रहे। आज मूल K&R कोड कम ही दिखता है। दिखे भी, तो वह अजीब लगेगा, जैसे आधुनिक अंग्रेज़ी के पाठकों को Middle English अजीब लगती है।
C89, ANSI C, C90
1989 में अमेरिकन नेशनल स्टैंडर्ड्स इंस्टीट्यूट (ANSI) ने C भाषा का एक स्पेसिफ़िकेशन तैयार किया जिसने C के लिए वह लय तय कर दी जो आज तक क़ायम है। एक साल बाद, बागडोर इंटरनेशनल ऑर्गनाइज़ेशन फ़ॉर स्टैंडर्डाइज़ेशन (ISO) को सौंप दी गई, जिसने हू-ब-हू वही C90 बनाया।
C95
C89 में एक कम ज़िक्र किया जाने वाला जोड़, जिसमें वाइड कैरेक्टर का समर्थन शामिल था।
C99
भाषा में ढेर सारे जोड़ों के साथ पहला बड़ा बदलाव। ज़्यादातर लोगों को जो चीज़ याद रहती है वह // शैली के कमेंट का जुड़ना है। यह इस लेखन के समय इस्तेमाल में C का सबसे लोकप्रिय संस्करण है।
C11
इस बड़े संस्करण अपडेट में यूनिकोड समर्थन और मल्टी-थ्रेडिंग शामिल हैं। ध्यान रहे कि अगर आप भाषा की इन सुविधाओं का इस्तेमाल करने लगे, तो उन जगहों के साथ पोर्टेबिलिटी की क़ुर्बानी दे रहे हो सकते हैं जो C99 की दुनिया में अटकी हैं। पर सच कहूँ तो 1999 को अब काफ़ी वक़्त हो चुका है।
C17, C18
C11 का बग-फ़िक्स अपडेट। लगता है C17 आधिकारिक नाम है, पर प्रकाशन 2018 तक टल गया। जहाँ तक मैं समझ पाया हूँ, ये दोनों नाम आपस में बदले जा सकते हैं, पर C17 को तरजीह दी जाती है।
C23
सबसे नया स्पेसिफ़िकेशन।
[i[gcc compiler]<]आप GCC को इनमें से किसी एक स्टैंडर्ड का इस्तेमाल करने पर मजबूर कर सकते हैं, -std= कमांड लाइन आर्ग्युमेंट के ज़रिए। अगर आप चाहते हैं कि वह स्टैंडर्ड के मामले में सख़्त मिज़ाज रहे, तो -pedantic जोड़ दें।
उदाहरण के लिए:
gcc -std=c11 -pedantic foo.c
इस किताब के लिए मैं प्रोग्रामों को C23 के लिए, सारी वार्निंग चालू करके, कंपाइल करता हूँ: