सी, पायथन, जावास्क्रिप्ट, गो, रस्ट, सॉलिडिटी जैसी भाषाओं और कंप्यूटर साइंस के बुनियादी और गणित के उन्नत सिद्धांतों को हिंदी में पढ़ने के लिए संपर्क करें। हम वेबसाइट और सॉफ्टवेयर भी बनाते हैं।
Reema Thareja की "Introduction to C Programming" के विषय-क्रम पर आधारित, अपने शब्दों में विस्तार से लिखे गए नोट्स।
C प्रोग्रामिंग — संपूर्ण हिंदी नोट्स
Reema Thareja की "Introduction to C Programming" का अपने शब्दों में संक्षिप्त, आरामदायक संस्करण — भूमिका व औपचारिकताएँ हटाकर, सिर्फ़ असली कंटेंट।
अध्याय 1: प्रोग्रामिंग की दुनिया में पहला कदम
प्रोग्राम और सॉफ़्टवेयर
प्रोग्राम कंप्यूटर को दिया गया निर्देशों का क्रम है जो किसी काम को पूरा करवाता है। जब कई प्रोग्राम, उनके डेटा और दस्तावेज़ एक साथ जुड़कर कोई बड़ा काम करते हैं तो उसे सॉफ़्टवेयर कहते हैं। कंप्यूटर अपने आप में बस एक बेजान मशीन है; उसे जो करना है, वह बताना प्रोग्रामर का काम है। प्रोग्राम क्या है? कंप्यूटर को दिया गया निर्देशों का क्रम, जो किसी काम को पूरा करवाता है। प्रोग्राम बनाने की पूरी प्रक्रिया को Software Development Life Cycle (SDLC) कहते हैं:
SDLC — सॉफ़्टवेयर जन्म से बुढ़ापे तक
समस्या पहचान (Problem Identification): क्या बनाना है, किसके लिए, क्यों — यह अच्छे से समझ लेना। आधी गलतियाँ इसी चरण में होती हैं।
विश्लेषण (Analysis): इनपुट क्या होगा, आउटपुट क्या चाहिए, मशीन की सीमाएँ क्या हैं।
डिज़ाइन: दो औज़ार — एल्गोरिद्म (भाषा में लिखे कदम) और फ्लोचार्ट (चित्र में लॉजिक)।
कोडिंग: डिज़ाइन को C जैसी भाषा में उतारना।
टेस्टिंग व डिबगिंग: गलतियाँ ढूँढ़ना, उनकी जड़ तक जाना, सुधारना।
रख-रखाव: उपयोगकर्ताओं की शिकायतें सुनकर सुधार लगाते रहना।
समस्या पहचानना — क्या बनाना है, यह साफ़ होना चाहिए।
डिज़ाइन — Flowchart (चित्र द्वारा लॉजिक) और एल्गोरिद्म (कदम-दर-कदम लॉजिक) बनाना।
कोडिंग — डिज़ाइन को किसी प्रोग्रामिंग भाषा में लिखना।
टेस्टिंग व डिबगिंग — गलतियाँ (bugs) ढूँढ़ना और सुधारना।
रख-रखाव (Maintenance) — उपयोग के दौरान सुधार करते रहना।
एल्गोरिद्म के गुण
हर कदम साफ़ और अलग हो; कोई दो कदम एक दूसरे से न उलझे; शुरुआत और अंत निश्चित हों; सामान्य स्थितियों में वह निश्चित समय में रुके। एल्गोरिद्म बनाने के नियम: हर कदम साफ़ और अलग हो, कोई कदम बेमतलब न हो, शुरू और अंत निश्चित हो। उदाहरण के लिए दो संख्याओं का योग निकालने का एल्गोरिद्म: शुरू करो → दो संख्याएँ पढ़ो → जोड़ो → छापो → रुको।
उदाहरण — तीन संख्याओं में सबसे बड़ी: शुरू → तीनों संख्याएँ पढ़ो → पहली को सबसे बड़ी मान लो → दूसरी बड़ी निकले तो उसे सबसे बड़ी बनाओ → तीसरी बड़ी निकले तो उसे सबसे बड़ी बनाओ → छापो → रुको। फ्लोचार्ट के मुख्य चिह्न: अंडाकार (शुरू/अंत), समानांतर चतुर्भुज (इनपुट/आउटपुट), आयत (प्रोसेस), हीरा (निर्णय)।
फ्लोचार्ट के चिह्न
प्रोग्रामिंग भाषाओं की पीढ़ियाँ:
भाषाओं की पीढ़ियाँ और अनुवादक
पीढ़ी
भाषा
खासियत
1st
मशीन भाषा (0/1)
सीधा हार्डवेयर पर चलती है, बहुत कठिन
2nd
असेंबली
mnemonic जैसे ADD, MOV
3rd
High-level (C, Fortran, BASIC)
इंसानों के पास की भाषा
4th/5th
SQL, एक्सेल मैक्रो, AI-आधारित
क्या करना है बताओ, कैसे खुद तय होता है
अनुवादक दो तरह के:कंपाइलर पूरी फ़ाइल एक सांस में बदलता है (C, C++); गलती कहीं भी हो, पूरी फ़ाइल का अनुवाद रुक जाता है। इंटरप्रेटर लाइन-दर-लाइन चलाता है (Python, BASIC) — गलती पर वहीं रुककर बता देता है, पर धीमा होता है। कंपाइलर बनाम इंटरप्रेटर: कंपाइलर पूरा प्रोग्राम एक साथ अनुवाद करता है (C में यही होता है), इंटरप्रेटर लाइन-दर-लाइन चलाता है (Python, BASIC)।
अध्याय 2: C भाषा से परिचय
इतिहास
Dennis Ritchie ने 1972 में Bell Labs में UNIX लिखने के लिए C बनाया। इसकी जड़ें BCPL → B → C में हैं। 1989 में ANSI ने मानक (ANSI C) तय किया — इसलिए अलग-अलग कंपाइलर पर भी कोड लगभग एक जैसा चलता है। C की खोज Dennis Ritchie ने 1972 में Bell Labs में की, मुख्यतः UNIX ऑपरेटिंग सिस्टम बनाने के लिए। यह काफी हद तक मशीन से नज़दीक है, इसलिए तेज़ भी है और हार्डवेयर पर सीधा कंट्रोल भी देता है।
C क्यों खास
निचले स्तर की ताक़त: मेमोरी और बिट्स तक सीधी पहुँच, इसलिए ऑपरेटिंग सिस्टम, एम्बेडेड सिस्टम, ड्राइवर इसी में लिखे जाते हैं।
तेज़: कोड मशीन के बहुत क़रीब बनता है।
पोर्टेबल: एक बार लिखो, लगभग हर जगह चलाओ।
संरचित व मॉड्यूलर: फंक्शन में बँटा हुआ, समझने और सुधारने में आसान।
समृद्ध लाइब्रेरी: stdio.h, math.h, string.h जैसे तैयार फंक्शन।
आज भी Python और Java के अंदर का ढाँचा, Arduino से Linux तक — C की देन है। C के गुण: सरल व संक्षिप्त, संरचित (structured), मॉड्यूलर (फंक्शनों में बँटा हुआ), पोर्टेबल (एक मशीन का प्रोग्राम दूसरी पर छोटे बदलाव से चल जाता है), और ऑपरेटिंग सिस्टम लिखने की क्षमता।
पहला प्रोग्राम, हिस्सा-दर-हिस्सा
एक C प्रोग्राम का ढाँचा:
#include <stdio.h> /* प्रीप्रोसेसर आदेश */
int main() /* हर प्रोग्राम की शुरुआत यहीं से */ { printf("Namaste Duniya\n"); /* स्क्रीन पर छापता है */ return 0; /* सफलता का संकेत */ }
ज़रूरी नियम
हर कथन के बाद ;
{ } की गिनती मिलनी चाहिए
C case-sensitive है: sum ≠ Sum ≠ SUM
main() हर प्रोग्राम में एक ही बार
कमेंट दो तरह: /* बहु-लाइन */ और // एक-लाइन (C99 से) नियम जो याद रखने चाहिए:
हर कथन (statement) के अंत में अर्धविराम ;
कोड main() के { } ब्रेसिज़ के अंदर रहता है
C case-sensitive है — sum, Sum, SUM तीन अलग-अलग नाम हैं
खोलने वाली ब्रेस और बंद करने वाली ब्रेस की संख्या बराबर होनी चाहिए
प्रोग्राम चलने तक का सफ़र
सोर्स (.c) → प्रीप्रोसेसर (# वाली लाइनें निपटाई जाती हैं) → कंपाइलर (व्याकरण जाँच, मशीन कोड बनता है) → ऑब्जेक्ट फ़ाइल (.o/.obj) → लिंकर (printf जैसे लाइब्रेरी फंक्शन जुड़ते हैं) → एक्ज़ीक्यूटेबल (.exe) → लोडर (मेमोरी में रखकर CPU चलाता है)। प्रोग्राम के जन्म से चलने तक (Building & Running): सोर्स कोड (.c) → प्रीप्रोसेसर → कंपाइलर → असेंब्लर → ऑब्जेक्ट कोड (.obj) → लिंकर (+ लाइब्रेरी फंक्शन) → एक्ज़ीक्यूटेबल (.exe) → रन।
💡 Tip: कंपाइलर की गलतियाँ तीन तरह की होती हैं — syntax (व्याकरण भूल, जैसे ; छूटना), runtime (चलते हुए फटना, जैसे शून्य से भाग) और logical (कोड चलता है पर जवाब गलत)। आख़िरी वाली सबसे ख़तरनाक है, क्योंकि कंपाइलर उसे पकड़ता ही नहीं।
अध्याय 3: इनपुट और आउटपुट का प्रबंधन
डेटा टाइप और आकार (सामान्य 64-बिट मशीन पर)
चर (Variable) — मेमोरी में नाम वाला डिब्बा जिसमें वैल्यू बदल सकती है। C में हर चर का टाइप पहले बताना ज़रूरी है:
sizeof(int) लिखकर अपनी मशीन पर आकार खुद छाप देखें — यही भरोसेमंद तरीक़ा है।
चर, स्थिरांक और नामकरण
चर का नाम अक्षर या _ से शुरू हो; अंक शुरू में नहीं।
स्थिरांक दो तरह: const float PI = 3.14; या #define PI 3.14
Literal के प्रकार: पूर्णांक (25), दशमलव (3.14), अक्षर ('A'), स्ट्रिंग ("Hello")।
printf और scanf की अंदरूनी बात
int umar = 21; /* पूर्णांक */ float marks = 88.5; /* दशमलव */ char grade = 'A'; /* एक अक्षर */
scanf में & क्यों? क्योंकि वह माँगता है "किस पते पर जमा करूँ" — &umar वही पता देता है। बिना & लिखने पर वैल्यू कहीं भी जमा हो सकती है और प्रोग्राम फट सकता है।
प्रमुख फॉर्मेट स्पेसिफ़ायर
const कीवर्ड: const float PI = 3.14; — ऐसी "चर" जिसकी वैल्यू बदलना मना है।
कंसोल से बातचीत के दो मुख्य फंक्शन:
फंक्शन
काम
उदाहरण
printf()
आउटपुट दिखाना
printf("Sum = %d", s);
scanf()
इनपुट लेना
scanf("%d", &x);
सजावट: चौड़ाई और दशमलव
printf("%10.2f", 3.14159); → 10 जगहों का खाना, दशमलव के बाद 2 अंक: 3.14 (दाईं ओर अलाइन)। %010.2f खाली जगह शून्य से भरेगा। %-10d बाईं ओर अलाइन करेगा। फॉर्मेट स्पेसिफ़ायर जो बार-बार लगेंगे: %d (int), %f (float), %c (char), %lf (double), %s (स्ट्रिंग), %e (वैज्ञानिक रूप)।
getchar/putchar और gets जैसे साथी
ch = getchar(); एक अक्षर लेता है; putchar(ch); छापता है। gets() पुराना और ख़तरनाक है (सीमा की जाँच नहीं) — नया कोड fgets() करें।
💡 Tip: scanf में चर के आगे & (address-of) लगाना मत भूलिए — यह C की सबसे आम गलतियों में से एक है।
⚠️ Note: \n का मतलब "अगली लाइन की शुरुआत" है, और \0 का मतलब "स्ट्रिंग यहीं खत्म" — दोनों अलग चीज़ें हैं। %c में बराबर चिह्न नहीं लगता, जैसे printf("%5.2s", naam); जैसी चौड़ाई सिर्फ़ %s में समझदारी से चलती है। एस्केप सीक्वेंस: \n नई लाइन, \t टैब, \\ बैकस्लैश, \" दोहरा उद्धरण, \0 स्ट्रिंग का अंत।
प्रिंटफ़ में सजावट: फ़ील्ड चौड़ाई और दशमलव ऐसे तय होते हैं — printf("%8.2f", num); मतलब कुल 8 जगह, दशमलव के बाद 2 अंक।
अध्याय 4: ऑपरेटर और एक्सप्रेशन
अंकगणितीय
+ - * / %। दो int का भाग int ही होता है: 5/2 = 2। दशमलव चाहिए तो किसी एक को float बनाएँ: 5.0/2 = 2.5। % सिर्फ़ पूर्णांकों पर। ऑपरेटर वे चिह्न हैं जो वैल्यूओं पर काम करते हैं। मुख्य श्रेणियाँ:
वृद्धि-कमी (Increment/Decrement)
एक ही एक्सप्रेशन में एक चर पर दो बार ++ मत लगाइए (जैसे a = i++ + ++i;) — परिणाम कंपाइलर पर बदल सकता है।
अंकगणितीय (Arithmetic): + - * / % — % शेषफल देता है (7 % 3 = 1), सिर्फ़ पूर्णांकों पर।
💡 Tip: प्राथमिकता का सिरफ़िरा लगता है तो ब्रेकेट लगा दीजिए। ब्रेकेट से प्रोग्राम साफ़ भी पढ़ता है और गलती भी कम होती है।
याद रखिए: बिटवाइज़ पूर्णांकों पर चलता है, float पर नहीं। यही ऑपरेटर मास्क बनाने और एम्बेडेड कोड में लगता है। टाइप कास्टिंग (Type Conversion):
Implicit: छोटे टाइप खुद बड़े में बदल जाते हैं — int जब float से मिले तो खुद float बन जाता है। पर char ch = 1234; जैसे उदाहरण में ओवरफ़्लो होकर गलत वैल्यू आती है।
Explicit: avg = (float) total / count; — जब खुद बदलना चाहें, जैसे पूर्णांक भाग में दशमलव जवाब चाहिए।
sizeof और कंडीशनल
sizeof(float) → 4। chhota = (a < b) ? a : b; — शर्त सच तो : के बाएँ, झूठ तो दाएँ।
प्राथमिकता का पूरा श्रेणीक्रम
कठिन लगे तो ब्रेकेट — प्राथमिकता याद करने से ज़्यादा भरोसेमंद औज़ार है।
अध्याय 5: नियंत्रण कथन — भाग 1: निर्णय
टाइप कास्टिंग का विस्तार
Implicit (खुद-ब-खुद): int + float में int खुद float बन जाता है; char खुद int बनता है। पर संकीर्ण मोड़ में ओवरफ़्लो ख़ामोश रहता है: char ch = 1234; — 1234 के निचले 8 बिट रह जाते हैं और जवाब बदल जाता है।
Explicit (जान-बूझकर): आम तौर पर प्रोग्राम ऊपर से नीचे चलता है, पर अक्सर किसी शर्त पर रास्ता बदलना पड़ता है।
1. if कथन:
अध्याय 5: निर्णय कथन
if के सारे रूप
if (marks >= 33) printf("Pass!\n");
/* दो रास्ते */ 2. if-else:
if (n % 2 == 0) printf("Sam\n"); else printf("Visham\n");
/* सीढ़ी — ऊपर से नीचे पहली सच वाली शर्त जीतती है */ 3. else-if सीढ़ी (एक से कई शर्तें):
if (marks >= 90) grade = 'A'; else if (marks >= 75) grade = 'B'; else if (marks >= 60) grade = 'C'; else grade = 'F';
⚠️ Note: if (x = 5) लिखना शर्त नहीं, असाइनमेंट है — वैल्यू 5 सच मानी जाती है और if हमेशा चलता है। तुलना में == लगाइए। कुछ कंपाइलर if (5 = x) लिखने की सलाह देते हैं ताकि यह गलती फ़ौरन पकड़ी जाए। 4. नेस्टेड if: if के अंदर if — तीन संख्याओं में सबसे बड़ी ढूँढ़ने का क्लासिक तरीका।
switch-case
5. switch-case (मेनू जैसा बहु-रास्ता):
switch (choice) { case 1: printf("Jodna\n"); break; case 2: printf("Ghatana\n"); break; default: printf("Galat vikalp\n"); }
शर्त पूर्णांक या char पर लगती है (float पर नहीं)।
break नहीं तो आगे के case भी चलेंगे — जान-बूझकर कई case एक साथ काम देना इसी काम का सही उपयोग है।
रेंज या जटिल शर्त चाहिए तो if-else ही बेहतर।
कंडीशनल ऑपरेटर
grade = (marks >= 33) ? 'P' : 'F'; — एक-लाइन वाले निर्णय के लिए। जटिल शर्तों में इसे मोड़ने से पढ़ना मुश्किल हो जाता है, तो if-else ही लिखिए।
⚠️ Note: break लिखना भूल जाएँ तो नीचे वाले case भी चल उतरते हैं (fall-through)। यह खामोश गलती बड़े प्रोग्राम में दिन ख़राब कर देती है।
6. कंडीशनल ऑपरेटर: chhoti = (a < b) ? a : b; — छोटे निर्णय एक लाइन में।
अध्याय 6: लूप (दोहराव)
अध्याय 6: नियंत्रण कथन — भाग 2: लूप (दोहराव)
तीनों लूप की तुलना
लूप के तीन हिस्से हमेशा याद रखें: शुरुआत (initialization), शर्त (condition), अपडेट। तीनों लूप यहीं फ़र्क पर टिके हैं।
while — आधार
1. while लूप (पहले शर्त, फिर काम):
int i = 1; while (i <= 5) { printf("Kadam %d\n", i); i++; }
do-while — पहले चलो, फिर सोचो
2. do-while लूप (पहले काम, फिर शर्त — कम से कम एक बार ज़रूर चलता है):
int i = 1; do { printf("%d\n", i); i++; } while (i <= 5); /* यहाँ अर्धविराम ज़रूरी है */
मेनू-आधारित प्रोग्रामों की रीढ़ यही लूप है — उपयोगकर्ता को एक बार तो दिखाना ही है।
for — सब साफ़-सुथरा एक लाइन में
3. for लूप (सब कुछ एक लाइन में):
for (int i = 1; i <= 10; i++) printf("2 x %d = %d\n", i, 2*i);
तीनों हिस्से (शुरुआत; शर्त; अपडेट) एक जगह दिखते हैं, इसलिए भूलचूक कम होती है।
break, continue, goto
break — लूप छोड़कर बाहर।
continue — यह चक्कर छोड़ो, अगला चक्कर करो। जैसे 3 के गुणज छोड़कर छापना:
goto label; — मौक़े पर बचाता है (जैसे गहरे नेस्टेड लूप से एक झटके में निकलना), पर स्पैघेटी-कोड बनाता है। डिज़ाइन शुद्ध रखने के लिए तौबा करना ठीक है। जंप कथन: break (लूप छोड़कर बाहर), continue (यह चरण छोड़ो, अगला करो), goto (किसी लेबल पर उड़ान — ज़्यादातर बचना चाहिए, प्रोग्राम की लय बिगाड़ता है)।
नेस्टेड लूप और पैटर्न
नेस्टेड लूप: लूप के अंदर लूप — पैटर्न छापने का सिलसिला:
for (int i = 1; i <= 4; i++) { for (int j = 1; j <= i; j++) printf("* "); printf("\n"); } /* आउटपुट: * * * * * * * * * * */
अंदरूनी लूप का कुल चक्कर = बाहरी के हर चक्कर पर अंदरूनी के चक्कर गिनकर जोड़ें — 3D समझ यहीं बनती है।
💡 Tip: जब भी लूप लिखें, तीन जगह देखिए — शुरुआत सही है, शर्त कभी-कभी झूठ होगी, और अपडेट शर्त को झूठा बनाने की तरफ़ बढ़ा रहा है। तीनों ठीक हों तो अनंत लूप असंभव है। 💡 Tip: while(1) या for(;;) अनंत लूप बना देते हैं। जान-बूझकर बनाएँ तो अंदर break का इंतज़ाम ज़रूर रखें, वरना प्रोग्राम कभी रुकेगा नहीं।
अध्याय 7: ऐरे (Arrays)
अध्याय 7: ऐरे (Arrays) — एक नाम, कई डिब्बे
1-D ऐरे
मेमोरी में लगातार, इसलिए पहुँच तुरंत: num[i] का पता = शुरुआती पता + i × डिब्बे का आकार।
एक साथ लिखते समय आकार छोड़ भी सकते हैं: int a[] = {2, 4, 6}; — C खुद गिन लेगा। जब 100 नंबर रखने हों तो 100 चर नहीं बनाएँगे — ऐरे बनाएँगे: int marks[100]; मतलब मेमोरी में लगातार 100 डिब्बे, 0 से 99 तक गिनती।
क्लासिक काम
सबसे बड़ा ढूँढ़ना:
int num[5] = {10, 20, 30, 40, 50}; printf("%d", num[2]); /* छापेगा 30, क्योंकि गिनती 0 से */
उलटना: Linear Search: क्रम-क्रम तुलना; मिले तो जगह लौटाओ, न मिले तो -1। Bubble Sort: हर चक्कर में पास-वाले जोड़े तुलना जाते हैं और बड़ा नीचे की ओर सरकता है: छोटे डेटा के लिए ठीक, बड़े डेटा के लिए दक्ष तरीक़े (quick sort वगैरह) आगे मिलेंगे।
2-D ऐरे (मैट्रिक्स)
1-D ऐरे के मुख्य काम: सबसे बड़ा/छोटा ढूँढ़ना, उलटना (reverse), खोजना (linear search), छाँटना (bubble sort — पास-वाले जोड़े बार-बार तुलना कर आगे-पीछे होते हैं)।
मैट्रिक्स जोड़: संगत सेल जोड़ो; गुणा: पहली की पंक्ति × दूसरी का स्तंभ।
⚠️ Note: ऐरे सीमा से बाहर जाने पर C चुप रहता है — न कंपाइलर रोकता है, न रन पर आवाज़ आती है। पर num[15] पढ़ना-लिखना किसी दूसरे चर की मेमोरी रौंद देता है। लूप की शर्त हमेशा i < n जैसी रखें, i <= n नहीं। दो नेस्टेड लूप से पढ़-लिखा जाता है; मैट्रिक्स जोड़ और गुणा इसी के अनुप्रयोग हैं।
⚠️ Note: C का ऐरे अपनी सीमा (bound) की जाँच नहीं करता। 10 वाले ऐरे पर num[15] लिखना कंपाइलर रोकेगा नहीं — पर मेमोरी खराब हो सकती है। सीमा का ध्यान प्रोग्रामर को रखना है।
अध्याय 8: स्ट्रिंग
अध्याय 8: स्ट्रिंग (Strings) — अक्षरों की माला
स्ट्रिंग का असली चेहरा
C में स्ट्रिंग असल में char का ऐरे है, जिसके अंत में \0 (null वर्ण) जुड़ा रहता है:
char naam[10] = "Kamta"; /* मेमोरी में: K a m t a \0 */
\0 ही बताता है कि स्ट्रिंग कहाँ खत्म हुई। यह मौजूद न हो तो printf मेमोरी में यूंहीं भागता रहेगा — और गड़बड़ छापता रहेगा। इसलिए स्ट्रिंग लिखते समय कम से कम (अक्षर + 1) बाइट की जगह रखें।
लाइब्रेरी फंक्शन (string.h)
स्ट्रिंग के काम की लाइब्रेरी फंक्शन (string.h):
दो उपयोगी नुस्खे
स्ट्रिंग की लंबाई बिना strlen के: पलिंड्रोम: स्वर गिनना: 'a','e','i','o','u' से तुलना चलाकर किसी स्वर से मेल खाए तो गिनती बढ़ाएँ। शब्द गिनने हों तो जितनी बार स्पेस के बाद अक्षर आए, उतने शब्द।
फंक्शन
काम
strlen(s)
लंबाई
strcpy(d, s)
कॉपी
strcat(a, b)
जोड़ना
strcmp(a, b)
तुलना (0 मतलब बराबर)
strrev(s)
उलटना
strlwr / strupr
छोटे / बड़े अक्षर
💡 Tip: strcmp(a, b) == 0 लिखिए, if (a == b) नहीं — स्ट्रिंगों की तुलना == से नहीं होती। naam1 = naam2 भी नहीं चलेगा, कॉपी के लिए strcpy ही चाहिए। पलिंड्रोम जैसे उदाहरण: स्ट्रिंग और उसका उल्टा बराबर निकले तो पलिंड्रोम (जैसे "madam")। स्वर-व्यंजन गिनना, शब्द गिनना वगैरह — सब स्ट्रिंग ऐरे पर लूप चलाने के खेल हैं।
💡 Tip: "A" और 'A' एक नहीं — पहला स्ट्रिंग (2 बाइट: 'A' और '\0'), दूसरा बस एक char।
अध्याय 9: फंक्शन
अध्याय 9: फंक्शन (Functions) — काम बाँटो, दोहराव मिटाओ
क्यों और कैसे
फंक्शन से कोड बार-बार नहीं लिखना पड़ता, गलती एक जगह सुधारने से सब जगह ठीक हो जाती है, और बड़ा प्रोग्राम छोटे-छोटे समझदार टुकड़ों में बँट जाता है। फंक्शन नाम वाला कोड-टुकड़ा है जिसे बार-बार बुलाया जा सकता है। इससे प्रोग्राम छोटा, पढ़ने लायक और गलती-रहित बनता है। C में हर प्रोग्राम main() से शुरू होता है, जो दूसरे फंक्शनों को बुलाता है।
ढाँचा:
int jod(int a, int b) /* घोषणा: रिटर्न-टाइप, नाम, पैरामीटर */ { return a + b; /* शरीर (body) */ }
तीन टुकड़े ज़रूरी: (1) घोषणा/prototype — main से पहले int jod(int, int); (2) परिभाषा — असली शरीर (3) कॉल — बुलावा। परिभाषा main से पहले लिखी हो तो अलग घोषणा की ज़रूरत नहीं।
कैटेगरी
बुलावे के तीन तरीके:
Call by value — वैल्यू की कॉपी जाती है; फंक्शन में बदलाव से असली चर नहीं बदलता।
Call by reference — पता (pointer) भेजते हैं; फंक्शन असली चर को ही बदल देता है।
Recursion (पुनरावर्तन) — फंक्शन खुद को बुलाता है, जैसे फैक्टोरियल:
Call by value बनाम reference
{-C में "reference" का मतलब पता (pointer) भेजना है। ऐरे का नाम लिखते ही पता जाता है, इसलिए फंक्शन में ऐरे पर किया बदलाव असली ऐरे में रहता है।-}
### {-Recursion (पुनरावर्तन)-} long fact(int n) { if (n <= 1) return 1; /* बेस केस — रुकने की शर्त ज़रूरी */ return n * fact(n - 1); }
हर कॉल स्टैक पर अपनी एक परत जमाती है; बेस केस छूते ही परतें उलटे क्रम में जवाब जोड़ती हुई लौटती है। factorial, Fibonacci, टावर ऑफ़ हनोई, बाइनरी सर्च — इन सबका सबसे सहज हल रिकर्शन में है। पर सादावार पुनरावर्तन (जैसे Fibonacci) लूप से ज़्यादा धीमा भी हो सकता है — जगह और समय दोनों की कीमत है।
स्कोप, आयु और static
लोकल: ब्लॉक के अंदर पैदा, ब्लॉक के साथ मरता है।
ग्लोबल: शुरुआत से अंत तक सबको दिखता है। ज़रूरत के बिना न बनाएँ — बचाव छोड़कर गलती बढ़ाते हैं।
static: static int gin = 0; फंक्शन के बाद भी वैल्यू याद रखता है — बार-बार कॉल में बढ़ती हुई गिनती ऐसे बनती है।
⚠️ Note: रिकर्शन में बेस केस न हो तो कॉलों का सिलसिला रुकता नहीं और प्रोग्राम क्रैश हो जाता है।
स्कोप व आयु: लोकल चर फंक्शन खत्म होते ही मर जाते हैं; ग्लोबल चर पूरे प्रोग्राम में जीते हैं; static चर फंक्शन की पुरानी वैल्यू याद रखता है।
अध्याय 10: पॉइंटर
अध्याय 10: पॉइंटर (Pointers) — मेमोरी का पता
मूल बात
पॉइंटर वह चर है जिसमें किसी दूसरे चर का पता (address) रहता है:
int x = 25; int *p = &x; /* p में x का पता */ printf("%d", *p); /* 25 — पते पर पहुँचकर वैल्यू */
& — पता लो; * — पते पर जाओ।
पॉइंटर का भी टाइप होता है: int * के साथ ही int का पता रखें। टाइल गड़बड़ाए तो पढ़ते समय ग़लत बाइट गिनती होगी।
NULL — "अभी कहीं नहीं संकेत" का मानक तरीक़ा। पॉइंटर का इस्तेमाल से पहले if (p != NULL) जाँच अच्छी आदत है।
पॉइंटर के चार बड़े उपयोग
1. ऐरे के साथ: नाम ही पहले डिब्बे का पता है। *(p+i) यानी a[i]: 2. Call by reference: फंक्शन में असली चर बदलना (अध्याय 9 देखें)। 3. Swap जैसे काम: 4. Dynamic memory: रन के समय जितनी जगह चाहिए उतनी माँगें।
&x → x का पता
*p → पते पर रखी वैल्यू (dereference)
डायनामिक मेमोरी (stdlib.h)
पॉइंटर का असली उपयोग: फंक्शन में call by reference, ऐरे की तेज़ चाल (*(p+i) मतलब a[i]), डायनामिक मेमोरी।
डायनामिक मेमोरी (stdlib.h):
फंक्शन
काम
malloc(size)
बिना इनिशियलाइज़ खाली जगह
calloc(n, size)
शून्य भरी जगह
realloc()
जगह बड़ी-छोटी करना
free(ptr)
जगह वापस करना — भूलना नहीं!
⚠️ Note: free न करना = memory leak; ख़ाली हो चुके पॉइंटर का इस्तेमाल = dangling pointer; malloc की वैल्यू पढ़ने से पहले इनिशियलाइज़ करना ज़रूरी। ये तीनों C में क्रैश की सबसे आम जड़ें हैं। 💡 Tip: डायनामिक मेमोरी लेने के बाद free() न करना "memory leak" कहलाता है — प्रोग्राम चलता-चलता मेमोरी चाट जाता है।
अध्याय 11: स्ट्रक्चर, यूनियन और typedef
अध्याय 11: स्ट्रक्चर और यूनियन (Structures & Unions)
स्ट्रक्चर — एक इकाई में अलग-अलग टाइप
स्ट्रक्चर अलग-अलग टाइप के डेटा को एक नाम के नीचे बाँधता है — जैसे विद्यार्थी की जानकारी:
struct Vidhyarthi { int rno; char naam[20]; float marks; };
struct Vidhyarthi v1 = {101, "Kamta", 88.5}; printf("%s", v1.naam); /* डॉट से सदस्य तक पहुँच */
स्ट्रक्चर टाइप का ऐरा: struct Vidhyarthi kaksha[60]; — पूरी कक्षा का रिकॉर्ड। सदस्य तक: kaksha[i].marks।
नेस्टेड और पॉइंटर के साथ
पॉइंटर से सदस्य तक: ptr->naam (तीर ऑपरेटर) या (*ptr).naam। स्ट्रक्चर का ऐरे: struct Vidhyarthi class[60]; — 60 विद्यार्थियों का रिकॉर्ड। नेस्टेड स्ट्रक्चर: स्ट्रक्चर के अंदर दूसरा स्ट्रक्चर। फंक्शन को स्ट्रक्चर: पूरा स्ट्रक्चर (या उसका पॉइंटर) पैरामीटर बन सकता है।
struct Vidhyarthi v, *pv = &v; pv->rno = 101; /* तीर ऑपरेटर = (*pv).rno */ pv->janm.saal = 2005; यूनियन वही दिखता है, पर सारे सदस्य एक ही मेमोरी खाना बाँटते हैं — एक वक़्त पर एक ही सदस्य उपयोग में; जगह बचती है। typedef से छोटा नाम दें: typedef struct Vidhyarthi Vid; — अब हर जगह Vid लिखेंगे।
फंक्शन को स्ट्रक्चर
पूरा स्ट्रक्चर भेजें (कॉपी जाएगी) या पॉइंटर भेजें (बड़े रिकॉर्ड में समझदारी यही है — कॉपी का खर्च बचता है)।
यूनियन — एक ही जगह, कई रूप
सारे सदस्य एक ही मेमोरी साझा करते हैं — आकार सबसे बड़े सदस्य के बराबर। एक वक़्त पर भरोसेमंद वैल्यू एक ही सदस्य की होती है। जगह बचाने की ज़रूरत हो (जैसे हार्डवेयर रजिस्टर) तो यूनियन, वरना स्ट्रक्चर।
typedef
typedef struct Vidhyarthi Vid; — अब Vid v1; लिखेंगे। पढ़ने में हल्का, टाइप बदलने में एक जगह बदलाव।
अध्याय 12: फ़ाइल हैंडलिंग
अध्याय 12: फ़ाइल हैंडलिंग (File Handling)
क्यों ज़रूरी
अब तक का सारा डेटा RAM में था — प्रोग्राम बंद होते ही गायब। फ़ाइल में लिखा डेटा डिस्क पर हमेशा के लिए रहता है, दूसरे प्रोग्राम में भी खुलता है। अब तक का डेटा प्रोग्राम बंद होते ही गायब। फ़ाइल में सहेजने से डेटा हमेशा के लिए रहता है।
खोलना, लिखना, बंद करना
FILE *fp; fp = fopen("record.txt", "w"); /* मोड: r पढ़ना, w लिखना, a जोड़ना */ fprintf(fp, "Hello file\n"); fclose(fp); /* बंद करना ज़रूरी */
मुख्य बातें:
fopen फेल हो तो NULL देता है — जाँच करना अची आदत है।
fprintf / fscanf वैसे ही चलते हैं जैसे printf/scanf, बस पहला आर्ग्युमेंट फ़ाइल है।
fgetc/fputc, fgets/fputs अक्षर व लाइन दर लाइन पढ़ाई-लिखाई।
feof(fp) फ़ाइल के अंत की खबर देता है।
fseek / ftell / rewind फ़ाइल में इधर-उधर जाने के साधन।
⚠️ Note: "w" मोड में खोलते ही पुरानी फ़ाइल का सारा कंटेंट मिट जाता है — बिना सोचे-समझे "w" मत खोलिए। जोड़ना है तो "a"। टेक्स्ट बनाम बाइनरी मोड: टेक्स्ट फ़ाइल अक्षरों में, बाइनरी फ़ाइल ("wb", "rb") रॉ बाइट्स में — स्ट्रक्चर का पूरा रिकॉर्ड एक झटके में fwrite()/fread() से सहेजा जा सकता है।
पढ़ने-लिखने की फुलकिट
पूरी फ़ाइल पढ़ने का साँचा:
टेक्स्ट बनाम बाइनरी
टेक्स्ट फ़ाइल में सब कुछ अक्षर रूप में — नोटपैड में खुलती है। बाइनरी फ़ाइल ("wb") में रॉ बाइट्स — स्ट्रक्चर का पूरा रिकॉर्ड एक झटके में: छोटा, तेज़, पर आँखों से नहीं पढ़ी जाती।
अध्याय 13: प्रीप्रोसेसर और मैक्रो
प्रीप्रोसेसर कौन
कंपाइल से पहले चलने वाला अलगावा — # वाली लाइनें देखकर टेक्स्ट में ही बदलाव (paste, replace, शर्त-आधारित शामिल करना) कर देता है। इसकी नज़ में C की कोई गिनती नहीं — यह बस टेक्स्ट का मोची है। कंपाइल से पहले एक "प्रीप्रोसेसर" चलता है जो # वाली लाइनें निपटाता है:
निर्देश
निर्देश
काम
उदाहरण
#include
हेडर फ़ाइल जोड़ना
#include <stdio.h>
#define
मैक्रो/स्थिरांक
#define PI 3.14
#define (फंक्शन-जैसा)
छोटे फॉर्मूले
#define SQUARE(x) ((x)*(x))
#ifdef / #endif
शर्त पर कंपाइल
सुरक्षा व टेस्ट में काम
फंक्शन-जैसे मैक्रो
फंक्शन कॉल का खर्च नहीं — कोड सीधा जगह पर खुल जाता है। पर बिना ब्रेकेट के: #define SQUARE(x) x*x पर SQUARE(2+3) बनेगा 2+3*2+3 = 11 — सही जवाब 25 है। इसलिए हर तर्क पर और पूरे एक्सप्रेशन पर ब्रेकेट।
💡 Tip: मैक्रो में तर्कों के आसपास ब्रेकेट लगाइए — #define SQUARE(x) x*x में SQUARE(2+3) बनेगा 2+3*2+3 = 11, जबकि सही जवाब 25 है।
हेडर गार्ड — हेडर फ़ाइल का बिजू
एक ही हेडर दो बार शामिल हो तो घोषणाएँ दोहरी नहीं बनतीं।
पूर्व-परिभाषित मैक्रो
__LINE__ (लाइन नंबर), __FILE__ (फ़ाइल नाम), __DATE__, __TIME__ — डिबग संदेशों में काम आते हैं।
समेटकर एक नज़र में
आगे का रास्ता
यही अवधारणाएँ आगे असल काम आती हैं: लिंक्ड लिस्ट, स्टैक-क्यू, और प्रोजेक्ट-स्तरीय प्रोग्राम। हर अध्याय के अंत के अभ्यास-प्रश्न अवश्य हल करें — C में उँगलियों से लिखकर ही असली सीखना होता है।
आगे का रास्ता: लिंक्ड लिस्ट, स्टैक-क्यू, बाइनरी ट्री — इन सबकी ईंटें यही अध्याय हैं। पॉइंटर और स्ट्रक्चर को हाथ साफ़ कर लें, तो डेटा स्ट्रक्चर की दुनिया खुल जाती है।
ये नोट्स मूल पुस्तक के विषय-क्रम का अनुसरण करते हुए स्वतंत्र रूप से लिखे गए हैं; पूरे अभ्यास-प्रश्न और विस्तृत उदाहरणों के लिए मूल पुस्तक अवश्य देखें।ये नोट्स मूल पुस्तक के विषय-क्रम का अनुसरण करते हुए स्वतंत्र रूप से लिखे गए सार हैं। विस्तृत अभ्यास और पूरे उदाहरणों के लिए मूल पुस्तक अवश्य देखें।
————————————————— C प्रोग्रामिंग — संपूर्ण विस्तृत हिंदी नोट्स
Reema Thareja की "Introduction to C Programming" के विषय-क्रम पर आधारित, अपने शब्दों में विस्तार से लिखे गए नोट्स।
अध्याय 1: प्रोग्रामिंग की दुनिया में पहला कदम
प्रोग्राम और सॉफ़्टवेयर
प्रोग्राम कंप्यूटर को दिया गया निर्देशों का क्रम है जो किसी काम को पूरा करवाता है। जब कई प्रोग्राम, उनके डेटा और दस्तावेज़ एक साथ जुड़कर कोई बड़ा काम करते हैं तो उसे सॉफ़्टवेयर कहते हैं। कंप्यूटर अपने आप में बस एक बेजान मशीन है; उसे जो करना है, वह बताना प्रोग्रामर का काम है।
SDLC — सॉफ़्टवेयर जन्म से बुढ़ापे तक
समस्या पहचान (Problem Identification): क्या बनाना है, किसके लिए, क्यों — यह अच्छे से समझ लेना। आधी गलतियाँ इसी चरण में होती हैं।
विश्लेषण (Analysis): इनपुट क्या होगा, आउटपुट क्या चाहिए, मशीन की सीमाएँ क्या हैं।
डिज़ाइन: दो औज़ार — एल्गोरिद्म (भाषा में लिखे कदम) और फ्लोचार्ट (चित्र में लॉजिक)।
कोडिंग: डिज़ाइन को C जैसी भाषा में उतारना।
टेस्टिंग व डिबगिंग: गलतियाँ ढूँढ़ना, उनकी जड़ तक जाना, सुधारना।
रख-रखाव: उपयोगकर्ताओं की शिकायतें सुनकर सुधार लगाते रहना।
एल्गोरिद्म के गुण
हर कदम साफ़ और अलग हो; कोई दो कदम एक दूसरे से न उलझे; शुरुआत और अंत निश्चित हों; सामान्य स्थितियों में वह निश्चित समय में रुके।
उदाहरण — तीन संख्याओं में सबसे बड़ी: शुरू → तीनों संख्याएँ पढ़ो → पहली को सबसे बड़ी मान लो → दूसरी बड़ी निकले तो उसे सबसे बड़ी बनाओ → तीसरी बड़ी निकले तो उसे सबसे बड़ी बनाओ → छापो → रुको।
फ्लोचार्ट के चिह्न
चिह्न
आकार
काम
Terminal
अंडाकार
शुरू / अंत
I/O
समानांतर चतुर्भुज
इनपुट / आउटपुट
Process
आयत
गणना / काम
Decision
हीरा
हाँ-ना का फैसला
Flow
तीर वाली रेखा
बहाव की दिशा
भाषाओं की पीढ़ियाँ और अनुवादक
पीढ़ी
उदाहरण
विशेषता
1st
मशीन भाषा (0, 1)
हार्डवेयर से सीधी बात, पढ़ना लगभग असंभव
2nd
असेंबली (ADD, MOV)
mnemonic — याद रखने में आसान, फिर भी मशीन-निर्भर
3rd
C, Fortran, BASIC
इंसानी भाषा के पास; पोर्टेबल
4th
SQL, Oracle
"क्या करना है" बताओ, "कैसे" भाषा खुद तय करती है
5th
Prolog, LISP व आधुनिक AI-उपकरण
नियम व तर्क आधारित
अनुवादक दो तरह के:कंपाइलर पूरी फ़ाइल एक सांस में बदलता है (C, C++); गलती कहीं भी हो, पूरी फ़ाइल का अनुवाद रुक जाता है। इंटरप्रेटर लाइन-दर-लाइन चलाता है (Python, BASIC) — गलती पर वहीं रुककर बता देता है, पर धीमा होता है।
अध्याय 2: C भाषा से परिचय
इतिहास
Dennis Ritchie ने 1972 में Bell Labs में UNIX लिखने के लिए C बनाया। इसकी जड़ें BCPL → B → C में हैं। 1989 में ANSI ने मानक (ANSI C) तय किया — इसलिए अलग-अलग कंपाइलर पर भी कोड लगभग एक जैसा चलता है।
C क्यों खास
निचले स्तर की ताक़त: मेमोरी और बिट्स तक सीधी पहुँच, इसलिए ऑपरेटिंग सिस्टम, एम्बेडेड सिस्टम, ड्राइवर इसी में लिखे जाते हैं।
तेज़: कोड मशीन के बहुत क़रीब बनता है।
पोर्टेबल: एक बार लिखो, लगभग हर जगह चलाओ।
संरचित व मॉड्यूलर: फंक्शन में बँटा हुआ, समझने और सुधारने में आसान।
समृद्ध लाइब्रेरी: stdio.h, math.h, string.h जैसे तैयार फंक्शन।
आज भी Python और Java के अंदर का ढाँचा, Arduino से Linux तक — C की देन है।
पहला प्रोग्राम, हिस्सा-दर-हिस्सा
#include <stdio.h> /* 1. हेडर: printf की घोषणा यहाँ से */
int main() /* 2. निष्पादन यहीं से शुरू */ { /* 3. ब्लॉक शुरू */ printf("Namaste Duniya\n"); /* 4. कथन, अंत में ; */ return 0; /* 5. OS को बताना: सब ठीक रहा */ } /* 6. ब्लॉक खत्म */
ज़रूरी नियम
हर कथन के बाद ;
{ } की गिनती मिलनी चाहिए
C case-sensitive है: sum ≠ Sum ≠ SUM
main() हर प्रोग्राम में एक ही बार
कमेंट दो तरह: /* बहु-लाइन */ और // एक-लाइन (C99 से)
प्रोग्राम चलने तक का सफ़र
सोर्स (.c) → प्रीप्रोसेसर (# वाली लाइनें निपटाई जाती हैं) → कंपाइलर (व्याकरण जाँच, मशीन कोड बनता है) → ऑब्जेक्ट फ़ाइल (.o/.obj) → लिंकर (printf जैसे लाइब्रेरी फंक्शन जुड़ते हैं) → एक्ज़ीक्यूटेबल (.exe) → लोडर (मेमोरी में रखकर CPU चलाता है)।
💡 Tip: कंपाइलर की गलतियाँ तीन तरह की होती हैं — syntax (व्याकरण भूल, जैसे ; छूटना), runtime (चलते हुए फटना, जैसे शून्य से भाग) और logical (कोड चलता है पर जवाब गलत)। आख़िरी वाली सबसे ख़तरनाक है, क्योंकि कंपाइलर उसे पकड़ता ही नहीं।
अध्याय 3: इनपुट और आउटपुट का प्रबंधन
डेटा टाइप और आकार (सामान्य 64-बिट मशीन पर)
टाइप
आकार
दायरा / उदाहरण
char
1 बाइट
-128 से 127, या अक्षर 'A'
int
4 बाइट
लगभग ±2 अरब
float
4 बाइट
~7 अंकों तक शुद्धता
double
8 बाइट
~15 अंकों तक शुद्धता
void
—
"कुछ नहीं" — फंक्शन का कोई रिटर्न नहीं
sizeof(int) लिखकर अपनी मशीन पर आकार खुद छाप देखें — यही भरोसेमंद तरीक़ा है।
चर, स्थिरांक और नामकरण
चर का नाम अक्षर या _ से शुरू हो; अंक शुरू में नहीं।
स्थिरांक दो तरह: const float PI = 3.14; या #define PI 3.14
Literal के प्रकार: पूर्णांक (25), दशमलव (3.14), अक्षर ('A'), स्ट्रिंग ("Hello")।
printf और scanf की अंदरूनी बात
int umar; printf("Umar batao: "); /* छापना, उछालना नहीं */ scanf("%d", &umar); /* & ज़रूरी: scanf को पता चाहिए */ printf("Umar = %d\n", umar);
scanf में & क्यों? क्योंकि वह माँगता है "किस पते पर जमा करूँ" — &umar वही पता देता है। बिना & लिखने पर वैल्यू कहीं भी जमा हो सकती है और प्रोग्राम फट सकता है।
प्रमुख फॉर्मेट स्पेसिफ़ायर
स्पेसिफ़ायर
टाइप
उदाहरण
%d
int
printf("%d", 42);
%f
float
printf("%f", 3.14);
%lf
double (scanf में)
scanf("%lf", &d);
%c
char
printf("%c", 'A');
%s
स्ट्रिंग
printf("%s", naam);
%e / %g
वैज्ञानिक / छोटा रूप
2.5e+03
%%
% छापना
printf("100%%");
सजावट: चौड़ाई और दशमलव
printf("%10.2f", 3.14159); → 10 जगहों का खाना, दशमलव के बाद 2 अंक: 3.14 (दाईं ओर अलाइन)। %010.2f खाली जगह शून्य से भरेगा। %-10d बाईं ओर अलाइन करेगा।
getchar/putchar और gets जैसे साथी
ch = getchar(); एक अक्षर लेता है; putchar(ch); छापता है। gets() पुराना और ख़तरनाक है (सीमा की जाँच नहीं) — नया कोड fgets() करें।
⚠️ Note: \n का मतलब "अगली लाइन की शुरुआत" है, और \0 का मतलब "स्ट्रिंग यहीं खत्म" — दोनों अलग चीज़ें हैं। %c में बराबर चिह्न नहीं लगता, जैसे printf("%5.2s", naam); जैसी चौड़ाई सिर्फ़ %s में समझदारी से चलती है।
अध्याय 4: ऑपरेटर और एक्सप्रेशन
अंकगणितीय
+ - * / %। दो int का भाग int ही होता है: 5/2 = 2। दशमलव चाहिए तो किसी एक को float बनाएँ: 5.0/2 = 2.5। % सिर्फ़ पूर्णांकों पर।
वृद्धि-कमी (Increment/Decrement)
int a = 5, b; b = a++; /* b = 5, फिर a = 6 — पहले इस्तेमाल, बाद में बढ़ो */ b = ++a; /* a पहले बढ़े, b = नई वैल्यू */
एक ही एक्सप्रेशन में एक चर पर दो बार ++ मत लगाइए (जैसे a = i++ + ++i;) — परिणाम कंपाइलर पर बदल सकता है।
असाइनमेंट
x = 5; के अलावा संयोजन: x += 3 (x = x+3), x -= 3, x *= 3, x /= 3, x %= 3।
संबंधीय और लॉजिकल
if (umar >= 18 && nagrik == 'Y') /* और */ if (marks < 33 || absent) /* या */ if (!found) • उलटना */
संबंधीय ऑपरेटर 1 या 0 देते हैं, इसलिए printf("%d", 5 > 3); छापेगा 1। शॉर्ट-सर्किट: && में बाईं शर्त झूठ निकली तो दाईं देखी ही नहीं जाती; || में बाईं सच निकली तो आगे नहीं देखा जाता। इसी से if (p != NULL && p->data > 0) जैसा सुरक्षित ढाँचा बनता है।
बिटवाइज़
ऑपरेटर
नाम
5 के साथ 3 पर
&
AND
101 & 011 = 001 → 1
|
OR
101 | 011 = 111 → 7
^
XOR
101 ^ 011 = 110 → 6
~
NOT
~5 → -6
<<
बाएँ शिफ़्ट
5 << 1 = 10 (दोगुना)
>>
दाएँ शिफ़्ट
5 >> 1 = 2 (आधा)
याद रखिए: बिटवाइज़ पूर्णांकों पर चलता है, float पर नहीं। यही ऑपरेटर मास्क बनाने और एम्बेडेड कोड में लगता है।
sizeof और कंडीशनल
sizeof(float) → 4। chhota = (a < b) ? a : b; — शर्त सच तो : के बाएँ, झूठ तो दाएँ।
कठिन लगे तो ब्रेकेट — प्राथमिकता याद करने से ज़्यादा भरोसेमंद औज़ार है।
टाइप कास्टिंग का विस्तार
Implicit (खुद-ब-खुद): int + float में int खुद float बन जाता है; char खुद int बनता है। पर संकीर्ण मोड़ में ओवरफ़्लो ख़ामोश रहता है: char ch = 1234; — 1234 के निचले 8 बिट रह जाते हैं और जवाब बदल जाता है।
Explicit (जान-बूझकर):
int total = 17, n = 5; float avg; avg = total / n; /* 3.0 — दोनों int, भाग कट गया */ avg = (float) total / n; /* 3.4 — सही तरीक़ा */
अध्याय 5: निर्णय कथन
if के सारे रूप
/* साधारण */ if (marks >= 33) printf("Pass!\n");
/* दो रास्ते */ if (n % 2 == 0) printf("Sam\n"); else printf("Visham\n");
/* सीढ़ी — ऊपर से नीचे पहली सच वाली शर्त जीतती है */ if (marks >= 90) grade = 'A'; else if (marks >= 75) grade = 'B'; else if (marks >= 60) grade = 'C'; else if (marks >= 33) grade = 'D'; else grade = 'F';
/* नेस्टेड — तीन में सबसे बड़ी */ if (a >= b) { if (a >= c) max = a; else max = c; } else { if (b >= c) max = b; else max = c; }
⚠️ Note: if (x = 5) लिखना शर्त नहीं, असाइनमेंट है — वैल्यू 5 सच मानी जाती है और if हमेशा चलता है। तुलना में == लगाइए। कुछ कंपाइलर if (5 = x) लिखने की सलाह देते हैं ताकि यह गलती फ़ौरन पकड़ी जाए।
switch-case
switch (din) { case 1: printf("Som\n"); break; case 2: printf("Mangal\n"); break; case 3: printf("Budh\n"); break; default: printf("Galat vikalp\n"); }
शर्त पूर्णांक या char पर लगती है (float पर नहीं)।
break नहीं तो आगे के case भी चलेंगे — जान-बूझकर कई case एक साथ काम देना इसी काम का सही उपयोग है।
रेंज या जटिल शर्त चाहिए तो if-else ही बेहतर।
कंडीशनल ऑपरेटर
grade = (marks >= 33) ? 'P' : 'F'; — एक-लाइन वाले निर्णय के लिए। जटिल शर्तों में इसे मोड़ने से पढ़ना मुश्किल हो जाता है, तो if-else ही लिखिए।
अध्याय 6: लूप (दोहराव)
तीनों लूप की तुलना
लूप
शर्त कब जाँची
कम से कम बार
सबसे सही कब
while
पहले
0
जब दोहराव की गिनती पहले से न पता हो
do-while
बाद में
1
मेनू दिखाना, इनपुट माँगना
for
पहले
0
गिनती पता हो (N बार चलाना)
while — आधार
int i = 1; /* शुरुआत */ while (i <= 5) { /* शर्त */ printf("Kadam %d\n", i); i++; /* अपडेट — भूले तो अनंत लूप */ }
do-while — पहले चलो, फिर सोचो
char ch; do { printf("Jari rakhna? (y/n): "); scanf(" %c", &ch); } while (ch == 'y'); /* अर्धविराम यहाँ ज़रूरी */
मेनू-आधारित प्रोग्रामों की रीढ़ यही लूप है — उपयोगकर्ता को एक बार तो दिखाना ही है।
for — सब साफ़-सुथरा एक लाइन में
for (int i = 1; i <= 10; i++) printf("2 x %d = %d\n", i, 2*i);
तीनों हिस्से (शुरुआत; शर्त; अपडेट) एक जगह दिखते हैं, इसलिए भूलचूक कम होती है।
break, continue, goto
break — लूप छोड़कर बाहर।
continue — यह चक्कर छोड़ो, अगला चक्कर करो। जैसे 3 के गुणज छोड़कर छापना:
for (int i = 1; i <= 10; i++) { if (i % 3 == 0) continue; printf("%d ", i); /* आउटपुट: 1 2 4 5 7 8 10 */ }
goto label; — मौक़े पर बचाता है (जैसे गहरे नेस्टेड लूप से एक झटके में निकलना), पर स्पैघेटी-कोड बनाता है। डिज़ाइन शुद्ध रखने के लिए तौबा करना ठीक है।
नेस्टेड लूप और पैटर्न
/* त्रिभुज */ for (int i = 1; i <= 4; i++) { for (int j = 1; j <= i; j++) printf("* "); printf("\n"); } /* गुणा तालिका — i चले पंक्तियाँ, j स्तंभ */ for (int i = 1; i <= 3; i++) { for (int j = 1; j <= 3; j++) printf("%4d", i*j); printf("\n"); }
अंदरूनी लूप का कुल चक्कर = बाहरी के हर चक्कर पर अंदरूनी के चक्कर गिनकर जोड़ें — 3D समझ यहीं बनती है।
💡 Tip: जब भी लूप लिखें, तीन जगह देखिए — शुरुआत सही है, शर्त कभी-कभी झूठ होगी, और अपडेट शर्त को झूठा बनाने की तरफ़ बढ़ा रहा है। तीनों ठीक हों तो अनंत लूप असंभव है।
अध्याय 7: ऐरे (Arrays)
1-D ऐरे
int num[5] = {10, 20, 30, 40, 50}; /* असल में 5 डिब्बे: num[0]..num[4] */ printf("%d", num[2]); /* 30 */
मेमोरी में लगातार, इसलिए पहुँच तुरंत: num[i] का पता = शुरुआती पता + i × डिब्बे का आकार।
एक साथ लिखते समय आकार छोड़ भी सकते हैं: int a[] = {2, 4, 6}; — C खुद गिन लेगा।
क्लासिक काम
सबसे बड़ा ढूँढ़ना:
int max = a[0]; for (int i = 1; i < n; i++) if (a[i] > max) max = a[i];
उलटना:
for (int i = 0; i < n/2; i++) { int t = a[i]; a[i] = a[n-1-i]; a[n-1-i] = t; }
Linear Search: क्रम-क्रम तुलना; मिले तो जगह लौटाओ, न मिले तो -1। Bubble Sort: हर चक्कर में पास-वाले जोड़े तुलना जाते हैं और बड़ा नीचे की ओर सरकता है:
for (int i = 0; i < n-1; i++) for (int j = 0; j < n-1-i; j++) if (a[j] > a[j+1]) { int t = a[j]; a[j] = a[j+1]; a[j+1] = t; }
छोटे डेटा के लिए ठीक, बड़े डेटा के लिए दक्ष तरीक़े (quick sort वगैरह) आगे मिलेंगे।
2-D ऐरे (मैट्रिक्स)
int mat[3][3]; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) scanf("%d", &mat[i][j]);
इनिशियलाइज़: int m[2][3] = {{1,2,3},{4,5,6}};
मैट्रिक्स जोड़: संगत सेल जोड़ो; गुणा: पहली की पंक्ति × दूसरी का स्तंभ।
⚠️ Note: ऐरे सीमा से बाहर जाने पर C चुप रहता है — न कंपाइलर रोकता है, न रन पर आवाज़ आती है। पर num[15] पढ़ना-लिखना किसी दूसरे चर की मेमोरी रौंद देता है। लूप की शर्त हमेशा i < n जैसी रखें, i <= n नहीं।
अध्याय 8: स्ट्रिंग
स्ट्रिंग का असली चेहरा
char naam[10] = "Kamta"; /* मेमोरी में: K a m t a \0 */
\0 ही बताता है कि स्ट्रिंग कहाँ खत्म हुई। यह मौजूद न हो तो printf मेमोरी में यूंहीं भागता रहेगा — और गड़बड़ छापता रहेगा। इसलिए स्ट्रिंग लिखते समय कम से कम (अक्षर + 1) बाइट की जगह रखें।
लाइब्रेरी फंक्शन (string.h)
फंक्शन
काम
उदाहरण का नतीजा
strlen(s)
\0 छोड़कर लंबाई
"Kamta" → 5
strcpy(d, s)
कॉपी
d में "Kamta"
strcat(a, b)
जोड़
"Kamta" + "Prasad"
strcmp(a, b)
तुलना
0 = बराबर; धनात्मक/ऋणात्मक = बड़ा/छोटा
strrev(s)
उलटना (कुछ कंपाइलरों में)
"atmaK"
strlwr/strupr
लघु/बड़ा रूप
"kamta" / "KAMTA"
strstr(s, sub)
उपस्थिति खोज
मिले तो स्थान, नहीं तो NULL
दो उपयोगी नुस्खे
स्ट्रिंग की लंबाई बिना strlen के:
int len = 0; while (naam[len] != '\0') len++;
पलिंड्रोम:
int i = 0, j = strlen(s) - 1, pal = 1; while (i < j) { if (s[i] != s[j]) { pal = 0; break; } i++; j--; }
स्वर गिनना: 'a','e','i','o','u' से तुलना चलाकर किसी स्वर से मेल खाए तो गिनती बढ़ाएँ। शब्द गिनने हों तो जितनी बार स्पेस के बाद अक्षर आए, उतने शब्द।
💡 Tip: strcmp(a, b) == 0 लिखिए, if (a == b) नहीं — स्ट्रिंगों की तुलना == से नहीं होती। naam1 = naam2 भी नहीं चलेगा, कॉपी के लिए strcpy ही चाहिए।
अध्याय 9: फंक्शन
क्यों और कैसे
फंक्शन से कोड बार-बार नहीं लिखना पड़ता, गलती एक जगह सुधारने से सब जगह ठीक हो जाती है, और बड़ा प्रोग्राम छोटे-छोटे समझदार टुकड़ों में बँट जाता है।
int jod(int a, int b) { /* परिभाषा */ return a + b; } int main() { int s = jod(3, 4); /* कॉल */ printf("%d\n", s); /* 7 */ }
तीन टुकड़े ज़रूरी: (1) घोषणा/prototype — main से पहले int jod(int, int); (2) परिभाषा — असली शरीर (3) कॉल — बुलावा। परिभाषा main से पहले लिखी हो तो अलग घोषणा की ज़रूरत नहीं।
कैटेगरी
रूप
उदाहरण
कोई लेना-देना नहीं
void menu(void) { ... }
ले, पर दे नहीं
void line(void) { printf("-----\n"); }
दे, पर न ले
int paani(void) { return 10; }
ले भी, दे भी
int jod(int a, int b)
Call by value बनाम reference
void badal(int x) { x = 100; } /* कॉपी बदली — असर नहीं */ void badalo(int *x) { *x = 100; } /* असली चर बदला */ int main() { int a = 5; badal(a); printf("%d\n", a); /* 5 */ badalo(&a); printf("%d\n", a); /* 100 */ }
C में "reference" का मतलब पता (pointer) भेजना है। ऐरे का नाम लिखते ही पता जाता है, इसलिए फंक्शन में ऐरे पर किया बदलाव असली ऐरे में रहता है।
Recursion (पुनरावर्तन)
long fact(int n) { if (n <= 1) return 1; /* बेस केस */ return n * fact(n - 1); /* बड़ा सवाल, छोटे सवाल पर टिका */ }
हर कॉल स्टैक पर अपनी एक परत जमाती है; बेस केस छूते ही परतें उलटे क्रम में जवाब जोड़ती हुई लौटती है। factorial, Fibonacci, टावर ऑफ़ हनोई, बाइनरी सर्च — इन सबका सबसे सहज हल रिकर्शन में है। पर सादावार पुनरावर्तन (जैसे Fibonacci) लूप से ज़्यादा धीमा भी हो सकता है — जगह और समय दोनों की कीमत है।
स्कोप, आयु और static
लोकल: ब्लॉक के अंदर पैदा, ब्लॉक के साथ मरता है।
ग्लोबल: शुरुआत से अंत तक सबको दिखता है। ज़रूरत के बिना न बनाएँ — बचाव छोड़कर गलती बढ़ाते हैं।
static: static int gin = 0; फंक्शन के बाद भी वैल्यू याद रखता है — बार-बार कॉल में बढ़ती हुई गिनती ऐसे बनती है।
अध्याय 10: पॉइंटर
मूल बात
int x = 25; int *p = &x; /* p में x का पता */ printf("%u\n", p); /* पता छपेगा */ printf("%d\n", *p); /* 25 — पते पर पहुँच */ *p = 50; /* x भी 50 हो गया! */
& — पता लो; * — पते पर जाओ।
पॉइंटर का भी टाइप होता है: int * के साथ ही int का पता रखें। टाइल गड़बड़ाए तो पढ़ते समय ग़लत बाइट गिनती होगी।
NULL — "अभी कहीं नहीं संकेत" का मानक तरीक़ा। पॉइंटर का इस्तेमाल से पहले if (p != NULL) जाँच अच्छी आदत है।
पॉइंटर के चार बड़े उपयोग
1. ऐरे के साथ: नाम ही पहले डिब्बे का पता है। *(p+i) यानी a[i]:
int a[5] = {10,20,30,40,50}; int *p = a; printf("%d %d", *p, *(p+2)); /* 10 30 */
2. Call by reference: फंक्शन में असली चर बदलना (अध्याय 9 देखें)। 3. Swap जैसे काम:
void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; }
4. Dynamic memory: रन के समय जितनी जगह चाहिए उतनी माँगें।
डायनामिक मेमोरी (stdlib.h)
int *arr = (int*) malloc(50 * sizeof(int)); /* माँग */ if (arr == NULL) { /* मेमोरी नहीं मिली */ } arr[0] = 7; free(arr); /* लौटाना ज़रूरी */
फंक्शन
काम
malloc(n)
n बाइट कच्ची जगह (कूड़ा जैसी वैल्यूओं के साथ)
calloc(n, size)
n × size जगह, सब शून्य से भरी
realloc(p, n)
पहले से ली जगह का आकार बदलना
free(p)
जगह वापस सिस्टम को
⚠️ Note: free न करना = memory leak; ख़ाली हो चुके पॉइंटर का इस्तेमाल = dangling pointer; malloc की वैल्यू पढ़ने से पहले इनिशियलाइज़ करना ज़रूरी। ये तीनों C में क्रैश की सबसे आम जड़ें हैं।
अध्याय 11: स्ट्रक्चर, यूनियन और typedef
स्ट्रक्चर — एक इकाई में अलग-अलग टाइप
struct Vidhyarthi { int rno; char naam[20]; float marks; };
पूरा स्ट्रक्चर भेजें (कॉपी जाएगी) या पॉइंटर भेजें (बड़े रिकॉर्ड में समझदारी यही है — कॉपी का खर्च बचता है)।
यूनियन — एक ही जगह, कई रूप
union Data { int i; float f; char str[20]; };
सारे सदस्य एक ही मेमोरी साझा करते हैं — आकार सबसे बड़े सदस्य के बराबर। एक वक़्त पर भरोसेमंद वैल्यू एक ही सदस्य की होती है। जगह बचाने की ज़रूरत हो (जैसे हार्डवेयर रजिस्टर) तो यूनियन, वरना स्ट्रक्चर।
स्ट्रक्चर
यूनियन
मेमोरी
सब सदस्यों का जोड़
सबसे बड़ा सदस्य
एक साथ सब सदस्य
हाँ
नहीं (एक समय एक)
उपयोग
रिकॉर्ड बनाना
जगह बचाना
typedef
typedef struct Vidhyarthi Vid; — अब Vid v1; लिखेंगे। पढ़ने में हल्का, टाइप बदलने में एक जगह बदलाव।
अध्याय 12: फ़ाइल हैंडलिंग
क्यों ज़रूरी
अब तक का सारा डेटा RAM में था — प्रोग्राम बंद होते ही गायब। फ़ाइल में लिखा डेटा डिस्क पर हमेशा के लिए रहता है, दूसरे प्रोग्राम में भी खुलता है।
खोलना, लिखना, बंद करना
FILE *fp = fopen("record.txt", "w"); /* मोड तय */ if (fp == NULL) { /* जाँच ज़रूरी */ printf("File nahi khuli!\n"); return 1; } fprintf(fp, "Naam: Kamta\n"); fclose(fp); /* बंद करना ज़रूरी */
मोड
अर्थ
फ़ाइल न हो तो
"r"
पढ़ना
NULL देता है
"w"
लिखना (पुराना साफ़!)
नई बनाता है
"a"
अंत में जोड़ना
नई बनाता है
"r+"
पढ़ना+लिखना
NULL
"w+"
पढ़ना+लिखना (साफ़)
नई बनाता है
"a+"
पढ़ना+जोड़ना
नई बनाता है
"rb", "wb"...
बाइनरी रूप
—
⚠️ Note: "w" मोड में खोलते ही पुरानी फ़ाइल का सारा कंटेंट मिट जाता है — बिना सोचे-समझे "w" मत खोलिए। जोड़ना है तो "a"।
कंपाइल से पहले चलने वाला अलगावा — # वाली लाइनें देखकर टेक्स्ट में ही बदलाव (paste, replace, शर्त-आधारित शामिल करना) कर देता है। इसकी नज़ में C की कोई गिनती नहीं — यह बस टेक्स्ट का मोची है।
फंक्शन कॉल का खर्च नहीं — कोड सीधा जगह पर खुल जाता है। पर बिना ब्रेकेट के: #define SQUARE(x) x*x पर SQUARE(2+3) बनेगा 2+3*2+3 = 11 — सही जवाब 25 है। इसलिए हर तर्क पर और पूरे एक्सप्रेशन पर ब्रेकेट।
हेडर गार्ड — हेडर फ़ाइल का बिजू
#ifndef MERI_FILE_H #define MERI_FILE_H /* घोषणाएँ यहाँ */ #endif
एक ही हेडर दो बार शामिल हो तो घोषणाएँ दोहरी नहीं बनतीं।
पूर्व-परिभाषित मैक्रो
__LINE__ (लाइन नंबर), __FILE__ (फ़ाइल नाम), __DATE__, __TIME__ — डिबग संदेशों में काम आते हैं।
समेटकर एक नज़र में
अध्याय
एक-लाइन सार
1
SDLC, एल्गोरिद्म, फ्लोचार्ट — सोचने की नींव
2
C का ढाँचा और कंपाइल-रन की प्रक्रिया
3
टाइप, चर, printf/scanf — मशीन से संवाद
4
ऑपरेटर, प्राथमिकता, कास्टिंग
5
if-else, switch — निर्णय
6
लूप और जंप — दोहराव पर नियंत्रण
7
ऐरे — एक नाम, लगातार डिब्बे
8
स्ट्रिंग — \0 पर टिकी माला
9
फंक्शन, value/reference, रिकर्शन
10
पॉइंटर — मेमोरी का पता और malloc/free
11
स्ट्रक्चर/यूनियन — असली रिकॉर्ड
12
फ़ाइल — डेटा की स्थायी दुकान
13
प्रीप्रोसेसर — कंपाइल से पहले का मोची
आगे का रास्ता: लिंक्ड लिस्ट, स्टैक-क्यू, बाइनरी ट्री — इन सबकी ईंटें यही अध्याय हैं। पॉइंटर और स्ट्रक्चर को हाथ साफ़ कर लें, तो डेटा स्ट्रक्चर की दुनिया खुल जाती है।
ये नोट्स मूल पुस्तक के विषय-क्रम का अनुसरण करते हुए स्वतंत्र रूप से लिखे गए हैं; पूरे अभ्यास-प्रश्न और विस्तृत उदाहरणों के लिए मूल पुस्तक अवश्य देखें।