सी, पायथन, जावास्क्रिप्ट, गो, रस्ट, सॉलिडिटी जैसी भाषाओं और कंप्यूटर साइंस के बुनियादी और गणित के उन्नत सिद्धांतों को हिंदी में पढ़ने के लिए संपर्क करें। हम वेबसाइट और सॉफ्टवेयर भी बनाते हैं।
<!-- Beej's guide to C
# vim: ts=4:sw=4:nosi:et:tw=72
-->
ऐरे {#arrays}
“ऐरे के इंडेक्स 0 से शुरू होने चाहिए या 1 से? मेरा 0.5 का समझौता बिना,
मेरे ख़याल से, उचित विचार के ख़ारिज कर दिया गया।”
---स्टैन केली-बूटल, कंप्यूटर वैज्ञानिक
[i[Arrays]<]
सौभाग्य से, सी में ऐरे हैं। मतलब, मुझे पता है कि इसे निम्न-स्तरीय
भाषा^[आजकल तो ख़ैर, यही माना जाता है।] माना जाता है, फिर भी कम से कम ऐरे की
अवधारणा इसमें अंतर्निहित है। और चूँकि बहुत-सी भाषाओं ने सी के सिंटैक्स से
प्रेरणा ली है, आप शायद ऐरे घोषित करने और इस्तेमाल करने के लिए [ और ] से
पहले ही परिचित होंगे।
पर सी में ऐरे बस नाम भर के हैं! जैसा हम आगे चलकर जानेंगे, सी में ऐरे महज़
सिंटैक्टिक शुगर हैं---भीतर गहराई में वे असल में पॉइंटर और वग़ैरह-वग़ैरह ही हैं।
घबरा जाइए! पर फ़िलहाल, हम उन्हें ऐरे की तरह ही इस्तेमाल करते हैं। उफ़्फ़।
आसान उदाहरण {#easy-example}
चलिए, सीधे एक उदाहरण बना डालते हैं:
[i[Arrays-->indexing]<]
#include <stdio.h>
int main(void)
{
int i;
float f[4]; // Declare an array of 4 floats
f[0] = 3.14159; // Indexing starts at 0, of course.
f[1] = 1.41421;
f[2] = 1.61803;
f[3] = 2.71828;
// Print them all out:
for (i = 0; i < 4; i++) {
printf("%f\n", f[i]);
}
}
जब आप कोई ऐरे घोषित करते हैं, तो उसे एक साइज़ देना पड़ता है। और साइज़ निश्चित
होना चाहिए^[फिर कहता हूँ, पूरी तरह सच नहीं, पर वेरिएबल-लेंथ ऐरे---जिनका मैं
असल में प्रशंसक नहीं हूँ---किसी और समय की कहानी हैं।]।
ऊपर के उदाहरण में हमने 4 float का एक ऐरे बनाया। घोषणा में वर्गाकार कोष्ठकों
के भीतर का मान हमें यही बताता है।
आगे की पंक्तियों में हम ऐरे के मानों तक पहुँचते हैं, उन्हें सेट करते हैं या
पढ़ते हैं, फिर से वर्गाकार कोष्ठकों के साथ।
[i[Arrays-->indexing]>]
उम्मीद है कि यह आपकी जानी-पहचानी भाषाओं जैसा ही लग रहा होगा!
ऐरे की लंबाई पता करना {#getting-the-length-of-an-array}
[i[Arrays-->getting the length]<]
आप नहीं कर सकते...लगभग। सी यह जानकारी दर्ज नहीं करती^[चूँकि ऐरे भीतर से बस
ऐरे के पहले एलिमेंट के पॉइंटर हैं, इसलिए लंबाई दर्ज करने वाली कोई अतिरिक्त
जानकारी नहीं होती।]। इसे आपको किसी दूसरे वेरिएबल में अलग से सँभालना होगा।
जब मैं कहता हूँ "नहीं कर सकते", तो असल में मेरा मतलब है कि कुछ परिस्थितियाँ
हैं जिनमें आप कर सकते हैं। जिस स्कोप में ऐरे घोषित हुआ है, उसमें ऐरे के
एलिमेंट की संख्या पाने की एक तरकीब है। पर आम तौर पर, अगर आप ऐरे को फ़ंक्शन को
पास कर दें, तो यह वैसे काम नहीं करेगी जैसा आप चाहते हैं^[क्योंकि जब आप किसी
फ़ंक्शन को ऐरे पास करते हैं, तो असल में आप उस ऐरे के पहले एलिमेंट का पॉइंटर ही
पास कर रहे होते हैं, "पूरा" ऐरे नहीं।]।
आइए यह तरकीब देखें। बुनियादी विचार यह है कि आप ऐरे का [i[sizeof
operator-->with arrays]<]sizeof लें, और फिर उसे हर एलिमेंट के साइज़ से भाग दें
तो लंबाई मिल जाती है। उदाहरण के लिए, अगर int 4 बाइट का है और ऐरे 32 बाइट
लंबा है, तो उसमें $\frac{32}{4}$ यानी $8$ int की जगह होगी।
int x[12]; // 12 ints
printf("%zu\n", sizeof x); // 48 total bytes
printf("%zu\n", sizeof(int)); // 4 bytes per int
printf("%zu\n", sizeof x / sizeof(int)); // 48/4 = 12 ints!
अगर यह char का ऐरे है, तो ऐरे का sizeof ही एलिमेंट की संख्या है, क्योंकि
sizeof(char) की परिभाषा 1 है। बाक़ी हर चीज़ के लिए आपको हर एलिमेंट के साइज़
से भाग देना पड़ता है।
पर यह तरकीब सिर्फ़ उसी स्कोप में काम करती है जिसमें ऐरे परिभाषित हुआ था। अगर आप
ऐरे को फ़ंक्शन को पास करें, तो यह काम नहीं करती। चाहे आप फ़ंक्शन सिग्नेचर में
उसे "बड़ा" ही क्यों न लिख दें:
void foo(int x[12])
{
printf("%zu\n", sizeof x); // 8?! What happened to 48?
printf("%zu\n", sizeof(int)); // 4 bytes per int
printf("%zu\n", sizeof x / sizeof(int)); // 8/4 = 2 ints?? WRONG.
}
ऐसा इसलिए है कि जब आप फ़ंक्शन को ऐरे "पास" करते हैं, तो आप सिर्फ़ पहले एलिमेंट
का पॉइंटर पास कर रहे होते हैं, और sizeof उसी को मापता है। इस पर और बातें नीचे
फ़ंक्शन को एक-आयामी ऐरे पास करना खंड में हैं।
sizeof और ऐरे के साथ आप एक और काम कर सकते हैं: ऐरे घोषित किए बिना निश्चित
संख्या के एलिमेंट वाले ऐरे का साइज़ जान लेना। यह वैसा ही है जैसे आप sizeof(int)
से int का साइज़ जान लेते हैं।
उदाहरण के लिए, 48 double के ऐरे के लिए कितने बाइट चाहिए होंगे, यह देखने के
लिए आप यह कर सकते हैं:
sizeof(double [48]);
[i[sizeof operator-->with arrays]>]
[i[Arrays-->getting the length]>]
ऐरे इनिशियलाइज़र {#array-initializers}
[i[Array initializers]<]
आप ऐरे को पहले से ही स्थिरांकों से इनिशियलाइज़ कर सकते हैं:
#include <stdio.h>
int main(void)
{
int i;
int a[5] = {22, 37, 3490, 18, 95}; // Initialize with these values
for (i = 0; i < 5; i++) {
printf("%d\n", a[i]);
}
}
आपके इनिशियलाइज़र में कभी भी ऐरे में उपलब्ध जगह से ज़्यादा आइटम नहीं होने
चाहिए, वरना कंपाइलर चिड़चिड़ा हो जाएगा:
foo.c: In function ‘main’:
foo.c:6:39: warning: excess elements in array initializer
6 | int a[5] = {22, 37, 3490, 18, 95, 999};
| ^~~
foo.c:6:39: note: (near initialization for ‘a’)
पर (मज़ेदार तथ्य!) आपके इनिशियलाइज़र में ऐरे की उपलब्ध जगह से कम आइटम हो सकते
हैं। ऐरे के बाक़ी एलिमेंट अपने-आप शून्य से इनिशियलाइज़ हो जाएँगे। यह सभी तरह के
ऐरे इनिशियलाइज़र पर सामान्य रूप से लागू होता है: अगर आपके पास इनिशियलाइज़र है,
तो जिस चीज़ को स्पष्ट रूप से कोई मान नहीं दिया गया, वह शून्य हो जाएगी।
int a[5] = {22, 37, 3490};
// is the same as:
int a[5] = {22, 37, 3490, 0, 0};
पूरे ऐरे को शून्य पर सेट करना हो, तो इनिशियलाइज़र में यह एक आम शॉर्टकट दिखता है:
int a[100] = {0};
जिसका मतलब है, "पहला एलिमेंट शून्य करो, और फिर बाक़ी को भी अपने-आप शून्य कर दो।"
आप इनिशियलाइज़र में मान के लिए इंडेक्स बताकर ख़ास ऐरे एलिमेंट भी सेट कर सकते
हैं! ऐसा करने पर सी ख़ुशी-ख़ुशी आगे के मानों को इनिशियलाइज़ करती जाएगी जब तक
इनिशियलाइज़र ख़त्म न हो जाए, और बाक़ी सबको 0 से भर देगी।
ऐसा करने के लिए इंडेक्स को वर्गाकार कोष्ठकों में रखिए, उसके बाद =, और फिर मान
सेट कीजिए।
यह रहा एक उदाहरण जहाँ हम ऐरे बनाते हैं:
int a[10] = {0, 11, 22, [5]=55, 66, 77};
क्योंकि हमने 55 के आरंभ के रूप में इंडेक्स 5 लिखा, ऐरे में नतीजे का डेटा यह है:
0 11 22 0 0 55 66 77 0 0
आप यहाँ सरल स्थिर एक्सप्रेशन भी डाल सकते हैं।
#define COUNT 5
int a[COUNT] = {[COUNT-3]=3, 2, 1};
जो हमें देता है:
0 0 3 2 1
आख़िर में, आप साइज़ छोड़कर सी से ऐरे का साइज़ इनिशियलाइज़र से भी निकलवा सकते हैं:
int a[3] = {22, 37, 3490};
// is the same as:
int a[] = {22, 37, 3490}; // Left the size off!
[i[Array initializers]>]
सीमा से बाहर! {#out-of-bounds}
[i[Arrays-->out of bounds]<]
सी आपको ऐरे की सीमा से बाहर जाकर पहुँचने से नहीं रोकती। हो सकता है वह चेतावनी
तक न दे।
चलिए ऊपर से उदाहरण चुराकर ऐरे के अंत से आगे भी प्रिंट करते रहें। इसमें सिर्फ़ 5
एलिमेंट हैं, पर हम 10 प्रिंट करके देखते हैं कि क्या होता है:
#include <stdio.h>
int main(void)
{
int i;
int a[5] = {22, 37, 3490, 18, 95};
for (i = 0; i < 10; i++) { // BAD NEWS: printing too many elements!
printf("%d\n", a[i]);
}
}
बाप रे! यह क्या है? दरअसल, ऐरे के अंत से आगे प्रिंट करने का नतीजा वह होता है जिसे
सी डेवलपर अनडिफ़ाइंड बिहेवियर कहते हैं। इस दैत्य के बारे में हम बाद में और
बात करेंगे, पर अभी इसका मतलब है, "आपने कुछ बुरा कर दिया है, और आपके प्रोग्राम के
चलने के दौरान कुछ भी हो सकता है।"
और "कुछ भी" से मेरा मतलब आम तौर पर शून्य मिलने, फ़ालतू संख्याएँ मिलने या क्रैश
होने जैसी चीज़ों से है। पर असल में सी का स्पेक कहता है कि इस परिस्थिति में
कंपाइलर को ऐसा कोड बनाने की छूट है जो कुछ भी कर दे^[मेमोरी प्रोटेक्शन के
ज़माने से पहले के अच्छे पुराने एमएस-डॉस के दिनों में, मैं कुछ ख़ास तौर पर
अत्याचारी सी कोड लिख रहा था जो जान-बूझकर हर तरह के अनडिफ़ाइंड बिहेवियर में
उलझता था। पर मुझे पता था कि मैं क्या कर रहा हूँ, और चीज़ें काफ़ी अच्छी चल रही थीं।
जब तक कि मुझसे एक चूक नहीं हुई जिसने सिस्टम को जाम कर दिया और, जैसा मुझे रीबूट
करने पर पता चला, मेरी सारी बायोस सेटिंग्स उड़ा दीं। वह मज़ेदार था। (उन मज़ेदार
दिनों के लिए @man को सलाम।)]।
छोटा रूप: ऐसा कुछ मत कीजिए जो अनडिफ़ाइंड बिहेवियर पैदा करे। कभी
नहीं^[अनडिफ़ाइंड बिहेवियर पैदा करने वाली बहुत-सी चीज़ें हैं, सिर्फ़ सीमा से बाहर
ऐरे तक पहुँचना ही नहीं। इसी से सी भाषा इतनी रोमांचक बनती है।]।
[i[Arrays-->out of bounds]>]
बहु-आयामी ऐरे {#multidimensional-arrays}
[i[Arrays-->multidimensional]<]
आप अपने ऐरे में जितने चाहें उतने आयाम जोड़ सकते हैं।
int a[10];
int b[2][7];
int c[4][5][6];
ये मेमोरी में [flw[रो-मेजर क्रम|Row-_and_column-major_order]] में रखे जाते हैं।
इसका मतलब है कि 2डी ऐरे में, सूची में दिया पहला इंडेक्स पंक्ति बताता है, और दूसरा
स्तंभ।
आप बहु-आयामी ऐरे पर इनिशियलाइज़र को एक-दूसरे में समेटकर भी इस्तेमाल कर सकते हैं:
#include <stdio.h>
int main(void)
{
int row, col;
int a[2][5] = { // Initialize a 2D array
{0, 1, 2, 3, 4},
{5, 6, 7, 8, 9}
};
for (row = 0; row < 2; row++) {
for (col = 0; col < 5; col++) {
printf("(%d,%d) = %d\n", row, col, a[row][col]);
}
}
}
और आप स्पष्ट इंडेक्स के साथ भी इनिशियलाइज़ कर सकते हैं:
// Make a 3x3 identity matrix
int a[3][3] = {[0][0]=1, [1][1]=1, [2][2]=1};
जो इस तरह का 2डी ऐरे बनाता है:
1 0 0
0 1 0
0 0 1
[i[Arrays-->multidimensional]>]
ऐरे और पॉइंटर {#arrays-and-pointers}
[i[Arrays-->as pointers]<]
[बेपरवाही से] तो... मैंने ऊपर शायद ज़िक्र किया था कि ऐरे गहराई में पॉइंटर ही हैं?
अब हमें इसमें थोड़ी उथली डुबकी लगा लेनी चाहिए ताकि चीज़ें पूरी तरह उलझी हुई न रहें।
आगे चलकर हम देखेंगे कि ऐरे और पॉइंटर के बीच असली रिश्ता क्या है, पर अभी मैं बस
ऐरे को फ़ंक्शन में पास करने पर नज़र डालना चाहता हूँ।
ऐरे का पॉइंटर पाना {#getting-a-pointer-to-an-array}
मैं आपको एक राज़ बताना चाहता हूँ। आम तौर पर, जब कोई सी प्रोग्रामर ऐरे के पॉइंटर की
बात करता है, तो वह ऐरे के पहले एलिमेंट के पॉइंटर की बात कर रहा होता
है^[तकनीकी रूप से यह ग़लत है, क्योंकि ऐरे के पॉइंटर और ऐरे के पहले एलिमेंट के
पॉइंटर के टाइप अलग होते हैं। पर वह पुल हम तब जलाएँगे जब वहाँ पहुँचेंगे।]।
तो चलिए ऐरे के पहले एलिमेंट का पॉइंटर पाते हैं।
#include <stdio.h>
int main(void)
{
int a[5] = {11, 22, 33, 44, 55};
int *p;
p = &a[0]; // p points to the array
// Well, to the first element, actually
printf("%d\n", *p); // Prints "11"
}
सी में यह इतना आम है कि भाषा हमें इसका शॉर्टहैंड देती है:
p = &a[0]; // p points to the array
// is the same as:
p = a; // p points to the array, but much nicer-looking!
अकेले ऐरे का नाम लिख देना ही ऐरे के पहले एलिमेंट का पॉइंटर पाने के बराबर है! आने
वाले उदाहरणों में हम इसका भरपूर इस्तेमाल करेंगे।
पर एक मिनट रुकिए---क्या p एक int* नहीं है? और *p हमें 11 देता है, ठीक
a[0] की तरह? हाँऽऽऽ। आपको झलक मिलने लगी है कि सी में ऐरे और पॉइंटर आपस में
कैसे जुड़े हैं। (हम इस पर पॉइंटर II अध्याय में, ऐरे/पॉइंटर
समतुल्यता के अंतर्गत, बहुत और बात करेंगे।)
[i[Arrays-->as pointers]>]
फ़ंक्शन को एक-आयामी ऐरे पास करना {#passing1darrays}
[i[Arrays-->passing to functions]<]
चलिए एक-आयामी ऐरे के साथ एक उदाहरण करते हैं। मैं कुछ फ़ंक्शन लिखने जा रहा हूँ
जिन्हें हम ऐरे पास कर सकें और जो अलग-अलग काम करें।
कुछ दिमाग़ उड़ा देने वाले फ़ंक्शन सिग्नेचर के लिए तैयार हो जाइए!
#include <stdio.h>
// Passing as a pointer to the first element
void times2(int *a, int len)
{
for (int i = 0; i < len; i++)
printf("%d\n", a[i] * 2);
}
// Same thing, but using array notation
void times3(int a[], int len)
{
for (int i = 0; i < len; i++)
printf("%d\n", a[i] * 3);
}
// Same thing, but using array notation with size
void times4(int a[5], int len)
{
for (int i = 0; i < len; i++)
printf("%d\n", a[i] * 4);
}
int main(void)
{
int x[5] = {11, 22, 33, 44, 55};
times2(x, 5);
times3(x, 5);
times4(x, 5);
}
फ़ंक्शन में ऐरे को पैरामीटर के रूप में लिखने के ये सारे तरीके एक जैसे हैं।
void times2(int *a, int len)
void times3(int a[], int len)
void times4(int a[5], int len)
सी के नियमित प्रयोगकर्ताओं में पहला तरीका सबसे ज़्यादा प्रचलित है, और कहीं ज़्यादा।
और असल में, आख़िरी स्थिति में तो कंपाइलर को इसकी भी परवाह नहीं कि आप कौन-सी
संख्या पास करते हैं (सिवाय इसके कि वह शून्य से बड़ी होनी चाहिए^[सी11 §6.7.6.2¶1
के अनुसार इसका शून्य से बड़ा होना ज़रूरी है। पर आपको कहीं-कहीं ऐसा कोड दिख सकता है
जिसमें struct के अंत में शून्य लंबाई के ऐरे घोषित हैं, और जीसीसी इस मामले में
ख़ास तौर पर उदार है, जब तक आप -pedantic के साथ कंपाइल न करें। यह शून्य-लंबाई
वाला ऐरे वेरिएबल-लेंथ स्ट्रक्चर बनाने की एक जुगाड़ू तरकीब थी। दुर्भाग्य से, तकनीकी
रूप से ऐसे ऐरे तक पहुँचना अनडिफ़ाइंड बिहेवियर है, भले ही वह लगभग हर जगह चल जाता था।
सी99 ने इसकी जगह एक सुपरिभाषित विकल्प स्थापित किया जिसे फ़्लेक्सिबल ऐरे मेंबर
कहते हैं, जिसके बारे में हम बाद में बात करेंगे।])। वह कुछ भी लागू नहीं करता।
यह कह चुकने के बाद, अब यह बता दूँ कि बहु-आयामी ऐरे को फ़ंक्शन में पास करते समय
फ़ंक्शन घोषणा में ऐरे का साइज़ असल में मायने रखता है, पर उस पर हम वापस आएँगे।
[i[Arrays-->passing to functions]>]
फ़ंक्शन में ऐरे को बदलना {#changing-arrays-in-functions}
[i[Arrays-->modifying within functions]<]
हम कह चुके हैं कि ऐरे भेस बदले हुए पॉइंटर ही हैं। इसका मतलब है कि अगर आप किसी
फ़ंक्शन को ऐरे पास करते हैं, तो संभवतः आप ऐरे के पहले एलिमेंट का पॉइंटर पास कर
रहे हैं।
पर अगर फ़ंक्शन के पास डेटा का पॉइंटर है, तो वह उस डेटा में फेरबदल कर सकता है! इसलिए
फ़ंक्शन ऐरे में जो बदलाव करता है, वे बाहर कॉलर में भी दिखाई देंगे।
यह रहा एक उदाहरण जहाँ हम फ़ंक्शन को ऐरे का पॉइंटर पास करते हैं, फ़ंक्शन उस ऐरे के
मानों में फेरबदल करता है, और वे बदलाव बाहर कॉलर में दिखाई देते हैं।
#include <stdio.h>
void double_array(int *a, int len)
{
// Multiply each element by 2
//
// This doubles the values in x in main() since x and a both point
// to the same array in memory!
for (int i = 0; i < len; i++)
a[i] *= 2;
}
int main(void)
{
int x[5] = {1, 2, 3, 4, 5};
double_array(x, 5);
for (int i = 0; i < 5; i++)
printf("%d\n", x[i]); // 2, 4, 6, 8, 10!
}
भले ही हमने ऐरे को पैरामीटर a के रूप में पास किया जो int* टाइप का है, देखिए
कि हम उसे a[i] से ऐरे नोटेशन में कैसे इस्तेमाल करते हैं! क्याऽऽऽ। यह पूरी तरह
मान्य है।
बाद में जब हम ऐरे और पॉइंटर के बीच समतुल्यता की बात करेंगे, तब देखेंगे कि यह सब
कहीं ज़्यादा समझ में आता है। अभी के लिए इतना जान लेना काफ़ी है कि फ़ंक्शन ऐरे में
ऐसे बदलाव कर सकते हैं जो बाहर कॉलर में दिखाई देते हैं।
[i[Arrays-->modifying within functions]>]
फ़ंक्शन को बहु-आयामी ऐरे पास करना {#passing-multidimensional-arrays-to-functions}
[i[Arrays-->passing to functions]<]
जब हम बहु-आयामी ऐरे की बात करते हैं, तो कहानी थोड़ी बदल जाती है। सी को सभी आयाम
(पहले को छोड़कर) जानने ज़रूरी हैं, ताकि उसके पास इतनी जानकारी रहे कि मान ढूँढ़ने
के लिए मेमोरी में कहाँ देखना है।
यह रहा एक उदाहरण जहाँ हम सभी आयामों के बारे में स्पष्ट हैं:
#include <stdio.h>
void print_2D_array(int a[2][3])
{
for (int row = 0; row < 2; row++) {
for (int col = 0; col < 3; col++)
printf("%d ", a[row][col]);
printf("\n");
}
}
int main(void)
{
int x[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
print_2D_array(x);
}
पर इस मामले में ये दोनों^[यह भी समतुल्य है: void print_2D_array(int (*a)[3]),
पर वह मैं अभी बताना नहीं चाहता।] समतुल्य हैं:
कंपाइलर को असल में सिर्फ़ दूसरा आयाम चाहिए, ताकि वह पता लगा सके कि पहले आयाम की
हर बढ़ोतरी पर मेमोरी में कितनी दूर छोड़ना है। आम तौर पर, उसे पहले को छोड़कर सभी
आयाम जानने की ज़रूरत होती है।
साथ ही, याद रखिए कि कंपाइलर कंपाइल-टाइम पर न्यूनतम सीमा-जाँच करता है (अगर आप
भाग्यशाली हों), और सी रनटाइम पर सीमा की ज़रा भी जाँच नहीं करती। कोई सीट बेल्ट
नहीं! ऐरे एलिमेंट तक सीमा से बाहर पहुँचकर क्रैश मत कीजिए!
[i[Arrays-->passing to functions]>] [i[Arrays]>]