सी, पायथन, जावास्क्रिप्ट, गो, रस्ट, सॉलिडिटी जैसी भाषाओं और कंप्यूटर साइंस के बुनियादी और गणित के उन्नत सिद्धांतों को हिंदी में पढ़ने के लिए संपर्क करें। हम वेबसाइट और सॉफ्टवेयर भी बनाते हैं।
<!-- Beej's guide to C
# vim: ts=4:sw=4:nosi:et:tw=72
-->
स्ट्रक्चर {#structs}
[i[struct keyword]<]
सी में हमारे पास struct नाम की एक चीज़ है, जो उपयोगकर्ता द्वारा परिभाषित किया
जा सकने वाला टाइप है और जिसमें डेटा के कई टुकड़े रखे जा सकते हैं, जो संभवतः अलग-अलग
टाइप के हों।
यह कई वेरिएबल को एक अकेले वेरिएबल में बाँध देने का सुविधाजनक तरीक़ा है। यह वेरिएबल
को फ़ंक्शन में पास करने के लिए फ़ायदेमंद हो सकता है (ताकि आपको कई की जगह बस एक
पास करना पड़े), और डेटा को व्यवस्थित करने और कोड को ज़्यादा पठनीय बनाने में
उपयोगी है।
अगर आप किसी और भाषा से आए हैं, तो आप क्लास और ऑब्जेक्ट के विचार से परिचित
हो सकते हैं। ये सी में मूल रूप से मौजूद नहीं हैं^[हालाँकि सी में मेमोरी की अलग-अलग
इकाइयों, जैसे int, को "ऑब्जेक्ट" कहा जाता है, पर वे ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग
के अर्थ में ऑब्जेक्ट नहीं हैं।]। आप struct को ऐसी क्लास समझ सकते हैं जिसमें
सिर्फ़ डेटा मेंबर हों, और कोई मेथड नहीं।
स्ट्रक्चर घोषित करना {#declaring-a-struct}
[i[struct keyword-->declaring]<]
आप अपने कोड में struct इस तरह घोषित कर सकते हैं:
struct car {
char *name;
float price;
int speed;
};
यह अक्सर किसी भी फ़ंक्शन के बाहर ग्लोबल स्कोप में किया जाता है, ताकि struct
सब जगह उपलब्ध रहे।
जब आप ऐसा करते हैं, तो आप एक नया टाइप बना रहे होते हैं। पूरा टाइप नाम struct car है। (सिर्फ़ car नहीं---वह नहीं चलेगा।)
अभी उस टाइप का कोई वेरिएबल नहीं है, पर हम कुछ घोषित कर सकते हैं:
struct car saturn; // Variable "saturn" of type "struct car"
और अब हमारे पास struct car टाइप का एक अनइनिशियलाइज़्ड वेरिएबल
saturn^[सैटर्न अमेरिका में इकोनॉमी कारों का एक लोकप्रिय ब्रांड था, जब तक कि
2008 के संकट ने उसका धंधा बंद नहीं करा दिया, जो हम जैसे प्रशंसकों के लिए
दुःखद रहा।] है।
हमें इसे इनिशियलाइज़ करना चाहिए! पर उन अलग-अलग फ़ील्ड के मान हम कैसे सेट करें?
कई दूसरी भाषाओं की तरह, जिन्होंने इसे सी से चुराया है, हम अलग-अलग फ़ील्ड तक
पहुँचने के लिए डॉट ऑपरेटर (.) इस्तेमाल करेंगे।
पहली पंक्तियों में हमने struct car के मान सेट किए, और फिर अगले हिस्से में उन्हें
प्रिंट किया।
[i[struct keyword-->declaring]>]
स्ट्रक्चर इनिशियलाइज़र {#struct-initializers}
[i[struct keyword-->initializers]<]
पिछले खंड का वह उदाहरण थोड़ा बोझिल था। उस struct वेरिएबल को इनिशियलाइज़ करने का
कोई बेहतर तरीक़ा ज़रूर होना चाहिए!
आप इसे इनिशियलाइज़र से कर सकते हैं, वेरिएबल परिभाषित करते समय फ़ील्ड के मान
उसी क्रम में डालकर जिसमें वे struct में आते हैं। (यह वेरिएबल के परिभाषित हो
जाने के बाद काम नहीं करेगा---इसे परिभाषा में ही होना होगा।)
struct car {
char *name;
float price;
int speed;
};
// Now with an initializer! Same field order as in the struct declaration:
struct car saturn = {"Saturn SL/2", 16000.99, 175};
printf("Name: %s\n", saturn.name);
printf("Price: %f\n", saturn.price);
printf("Top Speed: %d km\n", saturn.speed);
यह बात कि इनिशियलाइज़र में फ़ील्ड का क्रम वही होना ज़रूरी है, थोड़ी डरावनी है। अगर
कोई struct car में क्रम बदल दे, तो बाक़ी सारा कोड टूट सकता है!
हम अपने इनिशियलाइज़र में ज़्यादा स्पष्ट हो सकते हैं:
struct car saturn = {.speed=175, .name="Saturn SL/2"};
अब यह struct घोषणा के क्रम से स्वतंत्र है। जो निश्चित रूप से ज़्यादा सुरक्षित
कोड है।
ऐरे इनिशियलाइज़र की तरह ही, छूटे हुए किसी भी फ़ील्ड डेज़िग्नेटर को शून्य से
इनिशियलाइज़ कर दिया जाता है (इस मामले में वह .price होगा, जिसे मैंने छोड़ दिया
है)।
[i[struct keyword-->initializers]>]
फ़ंक्शन को स्ट्रक्चर पास करना {#passing-structs-to-functions}
[i[struct keyword-->passing and returning]<]
struct को फ़ंक्शन में पास करने के लिए आप कुछ चीज़ें कर सकते हैं।
struct पास करें।
struct का पॉइंटर पास करें।
याद कीजिए कि जब आप किसी फ़ंक्शन को कुछ पास करते हैं, तो फ़ंक्शन के काम करने के
लिए उस चीज़ की एक कॉपी बनती है, चाहे वह किसी पॉइंटर की कॉपी हो, int की,
struct की, या किसी भी चीज़ की।
मूलतः दो स्थितियाँ हैं जब आप struct का पॉइंटर पास करना चाहेंगे:
आपको फ़ंक्शन से चाहिए कि वह पास किए गए struct में बदलाव कर सके, और वे बदलाव
कॉलर में दिखें।
struct कुछ बड़ा है और उसे स्टैक पर कॉपी करना सिर्फ़ पॉइंटर कॉपी करने से
ज़्यादा महँगा है^[64-बिट सिस्टम पर पॉइंटर संभवतः 8 बाइट का होता है।]।
इन दो कारणों से, फ़ंक्शन को struct का पॉइंटर पास करना कहीं ज़्यादा आम है, हालाँकि
struct को सीधे पास करना किसी भी तरह अवैध नहीं है।
चलिए पॉइंटर पास करके देखते हैं, एक ऐसा फ़ंक्शन बनाकर जो आपको struct car का
.price फ़ील्ड सेट करने देगा:
#include <stdio.h>
struct car {
char *name;
float price;
int speed;
};
int main(void)
{
struct car saturn = {.speed=175, .name="Saturn SL/2"};
// Pass a pointer to this struct car, along with a new,
// more realistic, price:
set_price(&saturn, 799.99);
printf("Price: %f\n", saturn.price);
}
वहाँ हमारे आर्ग्युमेंट के टाइप देखकर ही आप set_price() का फ़ंक्शन सिग्नेचर बना
सकते होंगे।
saturn एक struct car है, इसलिए &saturn ज़रूर struct car का एड्रेस होगा,
यानी struct car का पॉइंटर, अर्थात struct car*।
यह नहीं चलेगा, क्योंकि डॉट ऑपरेटर सिर्फ़ struct पर काम करता है... वह struct के
पॉइंटर पर काम नहीं करता।
ठीक है, तो हम वेरिएबल c को डीरेफ़रेंस करके उसका पॉइंटर हटा सकते हैं ताकि struct
तक पहुँच सकें। struct car* को डीरेफ़रेंस करने पर वह struct car मिलता है जिसकी
ओर पॉइंटर इशारा करता है, जिस पर हमें डॉट ऑपरेटर इस्तेमाल कर पाना चाहिए:
void set_price(struct car *c, float new_price) {
(*c).price = new_price; // Works, but is ugly and non-idiomatic :(
}
और यह चलता है! पर ये सारे कोष्ठक और तारा-चिह्न टाइप करना थोड़ा भद्दा है। सी में
ऐरो ऑपरेटर नाम की एक सिंटैक्टिक शुगर है जो इसमें मदद करती है।
[i[struct keyword-->passing and returning]>]
ऐरो ऑपरेटर {#the-arrow-operator}
[i[-> arrow operator]<]
ऐरो ऑपरेटर struct के पॉइंटर में फ़ील्ड का हवाला देने में मदद करता है।
void set_price(struct car *c, float new_price) {
// (*c).price = new_price; // Works, but non-idiomatic :(
//
// The line above is 100% equivalent to the one below:
c->price = new_price; // That's the one!
}
तो फ़ील्ड तक पहुँचते समय हम डॉट कब इस्तेमाल करें और ऐरो कब?
अगर आपके पास struct है, तो डॉट (.) इस्तेमाल करें।
अगर आपके पास struct का पॉइंटर है, तो ऐरो (->) इस्तेमाल करें।
[i[-> arrow operator]>]
struct की कॉपी बनाना और उन्हें लौटाना {#copying-and-returning-structs}
[i[struct keyword-->copying]<]
यह रहा आपके लिए एक आसान!
बस एक से दूसरे में असाइन कर दीजिए!
struct car a, b;
b = a; // Copy the struct
और फ़ंक्शन से struct लौटाना (उसके पॉइंटर की जगह) भी पाने वाले वेरिएबल में ऐसी
ही कॉपी बनाता है।
यह "डीप कॉपी" नहीं है^[डीप कॉपीstruct के भीतर के पॉइंटरों का पीछा करके उनके
इशारे वाला डेटा भी कॉपी करती है। शैलो कॉपी बस पॉइंटरों को कॉपी करती है, उनके
इशारे वाली चीज़ों को नहीं। सी में डीप कॉपी की कोई बिल्ट-इन सुविधा नहीं है।]।
सारे फ़ील्ड जैसे हैं वैसे ही कॉपी होते हैं, पॉइंटरों सहित।
[i[struct keyword-->copying]>]
struct की तुलना करना {#comparing-structs}
[i[struct keyword-->comparing]<]
ऐसा करने का बस एक ही सुरक्षित तरीक़ा है: हर फ़ील्ड की एक-एक करके तुलना करें।