सरणियों और कार्यों से निपटने के दौरान एक पैटर्न है; यह पहली बार में देखना थोड़ा कठिन है।
सरणियों के साथ काम करते समय, निम्नलिखित को याद रखना उपयोगी होता है: जब एक सरणी अभिव्यक्ति सबसे संदर्भों में दिखाई देती है, तो अभिव्यक्ति का प्रकार स्पष्ट रूप से "एन-एलिमेंट सरणी ऑफ टी" से "पॉइंटर टू टी" में बदल जाता है, और इसका मान सेट होता है सरणी में पहले तत्व को इंगित करने के लिए। इस नियम के अपवाद तब होते हैं जब सरणी अभिव्यक्ति &या तो sizeofऑपरेटरों या ऑपरेटरों के एक ऑपरेंड के रूप में प्रकट होती है , या जब यह एक स्ट्रिंग शाब्दिक है जिसे घोषणा में इनिशियलाइज़र के रूप में उपयोग किया जा रहा है।
इस प्रकार, जब आप किसी फ़ंक्शन को तर्क के रूप में एरे एक्सप्रेशन के साथ कहते हैं, तो फ़ंक्शन एक पॉइंटर प्राप्त करेगा, न कि एरे:
int arr[10];
...
foo(arr);
...
void foo(int *arr) { ... }
यही कारण है कि आप "% s" से संबंधित तर्कों के लिए ऑपरेटर का उपयोग नहीं करते हैं :&scanf()
char str[STRING_LENGTH];
...
scanf("%s", str);
अंतर्निहित रूपांतरण के कारण, scanf()एक char *मान प्राप्त करता है जो strसरणी की शुरुआत को इंगित करता है । यह किसी भी फ़ंक्शन के लिए एक सरणी अभिव्यक्ति के साथ एक तर्क के रूप में कहा जाता है (बस किसी भी str*फ़ंक्शन *scanfऔर *printfफ़ंक्शन आदि के बारे में) सही है।
व्यवहार में, आप संभवतः किसी फ़ंक्शन को &ऑपरेटर के उपयोग से सरणी अभिव्यक्ति के साथ कभी नहीं कहेंगे , जैसे कि:
int arr[N];
...
foo(&arr);
void foo(int (*p)[N]) {...}
ऐसा कोड बहुत आम नहीं है; आपको फ़ंक्शन घोषणा में सरणी के आकार को जानना होगा, और फ़ंक्शन केवल विशिष्ट आकार के सरणियों के लिए कार्य करता है (T-10-सरणी सरणी के लिए सूचक एक 11-तत्व सरणी के लिए सूचक की तुलना में एक अलग प्रकार है T की)।
जब एक सरणी अभिव्यक्ति &ऑपरेटर के लिए एक ऑपरेंड के रूप में प्रकट होती है , तो परिणामस्वरूप अभिव्यक्ति का प्रकार "टी-एन-एलिमेंट ऑफ टी-पॉइंटर" होता है, या T (*)[N], जो पॉइंटर्स एरे से अलग होता है ( T *[N]) और एक पॉइंटर से बेस टाइप () T *)।
फ़ंक्शंस और पॉइंटर्स के साथ काम करते समय, याद रखने का नियम है: यदि आप एक तर्क के मूल्य को बदलना चाहते हैं और इसे कॉलिंग कोड में परिलक्षित करते हैं, तो आपको उस चीज़ को एक पॉइंटर पास करना होगा जिसे आप संशोधित करना चाहते हैं। फिर से, सरणियों ने बंदर के रिंच को काम में फेंक दिया, लेकिन हम पहले सामान्य मामलों से निपटेंगे।
याद रखें कि C मान द्वारा सभी फ़ंक्शन तर्क पास करता है; औपचारिक पैरामीटर वास्तविक पैरामीटर में मान की एक प्रति प्राप्त करता है, और औपचारिक पैरामीटर में कोई भी परिवर्तन वास्तविक पैरामीटर में परिलक्षित नहीं होता है। सामान्य उदाहरण एक स्वैप कार्य है:
void swap(int x, int y) { int tmp = x; x = y; y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);
आपको निम्न आउटपुट मिलेगा:
स्वैप से पहले: a = 1, b = 2
स्वैप के बाद: a = 1, b = 2
औपचारिक पैरामीटर xऔर yसे अलग वस्तुओं रहे हैं aऔर b, इसलिए करने के लिए परिवर्तन xऔर yमें नहीं दिखते aऔर b। जब से हम के मूल्यों को संशोधित करना चाहते aहैं और b, हम गुजरना होगा संकेत स्वैप समारोह के लिए उन्हें करने के लिए:
void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);
अब आपका आउटपुट होगा
स्वैप से पहले: a = 1, b = 2
स्वैप के बाद: a = 2, b = 1
ध्यान दें कि, स्वैप फ़ंक्शन में, हम xऔर के मूल्यों को नहीं बदलते हैं y, लेकिन क्या xऔर y इंगित करते हैं । लिखने *xसे लिखने के लिए अलग है x; हम xअपने आप में मूल्य को अपडेट नहीं कर रहे हैं , हम उस स्थान से xमूल्य प्राप्त करते हैं।
यह उतना ही सच है अगर हम एक पॉइंटर मान को संशोधित करना चाहते हैं; अगर हम लिखते हैं
int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);
फिर हम इनपुट पैरामीटर के मूल्य को संशोधित कर रहे हैं stream, न कि किस stream बिंदु पर , इसलिए परिवर्तन streamका मूल्य पर कोई प्रभाव नहीं है in; इसे काम करने के लिए, हमें एक पॉइंटर से पॉइंटर में जाना होगा:
int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);
फिर से, सरणियों ने बंदर के रिंच को काम में फेंक दिया। जब आप किसी फ़ंक्शन के लिए एक सरणी अभिव्यक्ति पास करते हैं, तो फ़ंक्शन जो प्राप्त करता है वह एक सूचक है। सरणी सबस्क्रिप्टिंग को किस प्रकार परिभाषित किया गया है, इसके कारण आप एक सूचक पर एक सबस्क्रिप्ट ऑपरेटर का उपयोग उसी तरह कर सकते हैं जिस प्रकार आप इसे किसी सरणी पर उपयोग कर सकते हैं:
int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}
ध्यान दें कि सरणी ऑब्जेक्ट असाइन नहीं किए जा सकते हैं; यानी, आप ऐसा कुछ नहीं कर सकते
int a[10], b[10];
...
a = b;
इसलिए आप सावधान रहना चाहते हैं जब आप एरियर्स के साथ काम कर रहे हैं; कुछ इस तरह
void (int (*foo)[N])
{
...
*foo = ...;
}
काम नहीं करेगा।