सी में एक फ़ंक्शन के तर्क के रूप में एक सरणी पास करना


86

मैंने एक फ़ंक्शन लिखा है जिसमें तर्क के रूप में सरणी है, और इसे निम्नानुसार सरणी के पास मानकर कॉल करें।

void arraytest(int a[])
{
    // changed the array a
    a[0]=a[0]+a[1];
    a[1]=a[0]-a[1];
    a[0]=a[0]-a[1];
}

void main()
{
    int arr[]={1,2};
    printf("%d \t %d",arr[0],arr[1]);
    arraytest(arr);
    printf("\n After calling fun arr contains: %d\t %d",arr[0],arr[1]);
}

हालांकि मैंने पाया कि मैं arraytest()मानों को पारित करके फ़ंक्शन कह रहा हूं , की मूल प्रति int arr[]बदल गई है।

क्या आप कृपया बता सकते हैं कि क्यों?


1
आप संदर्भ द्वारा सरणी पास कर रहे हैं लेकिन आप इसकी सामग्री को संशोधित कर रहे हैं - इसलिए आप डेटा में बदलाव क्यों देख रहे हैं
शॉन वाइल्ड

main()वापस लौटना चाहिए int
अंडरस्कोर_ड

जवाबों:


137

पैरामीटर के रूप में एक सरणी पास करते समय, यह

void arraytest(int a[])

इसका मतलब बिल्कुल वैसा ही है

void arraytest(int *a)

इसलिए आप मुख्य में मूल्यों को संशोधित कर रहे हैं

ऐतिहासिक कारणों के लिए, सरणियाँ प्रथम श्रेणी के नागरिक नहीं हैं और उन्हें मूल्य द्वारा पारित नहीं किया जा सकता है।


3
किन परिस्थितियों में कौन सा अंकन बेहतर है?
रेमन मार्टिनेज

20
@ रमन - मैं दूसरे विकल्प का उपयोग करूंगा, क्योंकि यह कम भ्रामक लगता है और बेहतर संकेत देता है कि आपको सरणी की प्रतिलिपि नहीं मिलती है।
बो पर्सन

2
क्या आप "ऐतिहासिक कारणों" की व्याख्या कर सकते हैं? मुझे लगता है कि मूल्यों से गुजरना एक प्रति और स्मृति की बर्बादी की आवश्यकता होगी .. धन्यवाद
Jacquelyn.Marquardt

5
@lucapozzobon - मूल रूप से C के पास कोई मान नहीं है, केवल एकल मानों को छोड़कर। यह structभाषा में जोड़े जाने तक नहीं था कि इसे बदल दिया गया था। और फिर सरणियों के नियमों को बदलने के लिए बहुत देर हो चुकी थी। पहले से ही उपयोगकर्ताओं के 10 थे। :-)
बो पर्सन

1
... का अर्थ है ठीक उसी तरह जैसे void arraytest(int a[1000])आदि आदि यहाँ विस्तृत उत्तर: stackoverflow.com/a/51527502/4561887
गेब्रियल स्टेपल्स

9

यदि आप किसी फ़ंक्शन में एक एकल-आयाम सरणी को एक तर्क के रूप में पारित करना चाहते हैं , तो आपको निम्नलिखित तीन तरीकों में से एक में एक औपचारिक पैरामीटर घोषित करना होगा और सभी तीन घोषणा विधियां समान परिणाम उत्पन्न करती हैं क्योंकि प्रत्येक कंपाइलर को बताता है कि पूर्णांक सूचक जा रहा है प्राप्त करना

int func(int arr[], ...){
    .
    .
    .
}

int func(int arr[SIZE], ...){
    .
    .
    .
}

int func(int* arr, ...){
    .
    .
    .
}

इसलिए, आप मूल मानों को संशोधित कर रहे हैं।

धन्यवाद !!!


मैं आपके दूसरे उदाहरण की तलाश में था, क्या आप विस्तार से बता सकते हैं कि प्रत्येक विधियों के क्या फायदे हैं?
पक

8

आप सरणी को कॉपी के रूप में पारित नहीं कर रहे हैं। यह केवल एक संकेतक है जो उस पते की ओर इशारा करता है जहां सरणी का पहला तत्व स्मृति में है।


7

आप सरणी के पहले तत्व का पता दे रहे हैं


7

सी में एरे को परिवर्तित किया जाता है, अधिकांश मामलों में, सरणी के पहले तत्व के लिए एक संकेतक के लिए। और फ़ंक्शंस में पारित किए गए विस्तार सरणियों में हमेशा पॉइंटर्स में परिवर्तित हो जाते हैं।

यहाँ K & R2nd का एक उद्धरण :

जब एक सरणी नाम किसी फ़ंक्शन को दिया जाता है, तो जो पास किया जाता है वह प्रारंभिक तत्व का स्थान होता है। तथाकथित फ़ंक्शन के भीतर, यह तर्क एक स्थानीय चर है, और इसलिए एक सरणी नाम पैरामीटर एक सूचक है, अर्थात, एक चर जिसमें एक पता होता है।

लिख रहे हैं:

void arraytest(int a[])

लिखने के समान अर्थ है:

void arraytest(int *a)

तो इसके बावजूद कि आप इसे स्पष्ट रूप से नहीं लिख रहे हैं, जैसा कि आप एक पॉइंटर पास कर रहे हैं और इसलिए आप मुख्य में मूल्यों को संशोधित कर रहे हैं।

अधिक के लिए मैं वास्तव में इसे पढ़ने का सुझाव देता हूं ।

इसके अलावा, आप SO पर अन्य उत्तर यहां पा सकते हैं


6

आप सरणी के पहले सदस्य की मेमोरी लोकेशन का मान पास कर रहे हैं।

इसलिए जब आप फ़ंक्शन के अंदर सरणी को संशोधित करना शुरू करते हैं, तो आप मूल सरणी को संशोधित कर रहे हैं।

याद है जो a[1]है *(a+1)


1
मुझे लगता है कि वहाँ () के लिए गायब हैं * a + 1 होना चाहिए * (a + 1)
ShinTakezou

@ शाहीन धन्यवाद, जब से मैं सी
एलेक्स

6

सी में, कुछ विशेष मामलों को छोड़कर, एक सरणी संदर्भ हमेशा सरणी के पहले तत्व के लिए एक पॉइंटर को "डिसकस" करता है। इसलिए, "मान द्वारा" एक सरणी पास करना संभव नहीं है। फ़ंक्शन कॉल में एक सरणी फ़ंक्शन को एक पॉइंटर के रूप में पारित किया जाएगा, जो कि संदर्भ द्वारा सरणी पास करने के लिए अनुरूप है।

संपादित करें: तीन ऐसे विशेष मामले हैं जहां एक सरणी एक सूचक के लिए क्षय नहीं करता है यह पहला तत्व है:

  1. sizeof aजैसा है वैसा नहीं है sizeof (&a[0])
  2. &aके रूप में ही नहीं है &(&a[0])(और नहीं के रूप में काफी समान &a[0])।
  3. char b[] = "foo"जैसा है वैसा नहीं है char b[] = &("foo")

यदि मैं किसी फ़ंक्शन के लिए एक सरणी पास करता हूं। उदाहरण के लिए कहें कि मैंने एक सरणी बनाई int a[10]और प्रत्येक तत्व को यादृच्छिक मान दिया। अब अगर मैं इस एरे को किसी फंक्शन का उपयोग करके int y[]या int y[10]या के पास int *yकरता हूँ। और फिर उस फंक्शन में मैं sizeof(y)आंसर का उपयोग करता हूँ तो बाइट्स पॉइंटर आवंटित किया गया है। तो इस मामले में, यह एक सूचक के रूप में क्षय होगा, यह हेल्पफुल होगा यदि आप इसे भी शामिल करते हैं। इस पोस्ट को
सूरज जैन

यदि मैं sizeofमूल रूप से परिभाषित एरे में फ़ंक्शन में काम का उपयोग करता हूं तो यह एक एरे के रूप में क्षय होगा, लेकिन अगर मैं अन्य फ़ंक्शन में पास हो जाता हूं तो sizeofऑपरेटर का उपयोग करें यह पॉइंटर के रूप में क्षय होगा।
सूरज जैन


मुझे पता है यह पुराना है। दो प्रश्न अगर किसी को भी यह देखने में होता है :) 1. @ थोमस्मिथ ने लिखा है कि जब यह एक सरणी है तो &aसमान नहीं है । ऐसा कैसे? मेरे परीक्षण कार्यक्रम में, दोनों एक ही होना दिखाते हैं, दोनों फ़ंक्शन में जहां सरणी घोषित की जाती है, और जब एक अलग फ़ंक्शन में पारित किया जाता है। 2. लेखक लिखता है कि " जैसा है वैसा नहीं है "। मेरे लिए, उत्तरार्द्ध भी संकलन नहीं करता है। यह सिर्फ मैं हूँ? &a[0]achar b[] = "foo"char b[] = &("foo")
अवीव कोहन

6

फ़ंक्शन के तर्क के रूप में एक बहुआयामी सरणी पास करना। एक मंद सरणी को तर्क के रूप में पारित करना कम या ज्यादा तुच्छ है। आइए 2 मंद सरणी से गुजरने के अधिक दिलचस्प मामले पर एक नज़र डालें। C में आप int **2 मंद सरणी के बजाय पॉइंटर टू पॉइंटर कंस्ट्रक्शन ( ) का उपयोग नहीं कर सकते हैं । आइए एक उदाहरण बनाते हैं:

void assignZeros(int(*arr)[5], const int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 5; j++) {
            *(*(arr + i) + j) = 0;
            // or equivalent assignment
            arr[i][j] = 0;
        }
    }

यहां मैंने एक फ़ंक्शन निर्दिष्ट किया है जो 5 पूर्णांक की एक सरणी के लिए पहले तर्क के रूप में लेता है। मैं किसी भी 2 डिम सरणी के रूप में पास कर सकता हूं जिसमें 5 कॉलम हैं:

int arr1[1][5]
int arr1[2][5]
...
int arr1[20][5]
...

आपको अधिक सामान्य फ़ंक्शन को परिभाषित करने के लिए एक विचार आ सकता है जो किसी भी 2 मंद सरणी को स्वीकार कर सकता है और फ़ंक्शन हस्ताक्षर को निम्नानुसार बदल सकता है:

void assignZeros(int ** arr, const int rows, const int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            *(*(arr + i) + j) = 0;
        }
    }
}

यह कोड संकलित करेगा, लेकिन पहले फ़ंक्शन की तरह ही मानों को असाइन करने का प्रयास करते समय आपको एक रनटाइम त्रुटि मिलेगी। तो सी में एक बहुआयामी सरणियों संकेत करने के लिए संकेत के रूप में ही नहीं हैं ... संकेत करने के लिए। एक int(*arr)[5]5 तत्वों की सरणी के लिए सूचक, एक है int(*arr)[6] 6 तत्वों की सरणी के लिए सूचक है, और वे विभिन्न प्रकार के एक संकेत दिए गए हैं!

खैर, उच्च आयामों के लिए फ़ंक्शन तर्क कैसे परिभाषित करें? सरल, हम सिर्फ पैटर्न का पालन करते हैं! यहां 3 आयामों की एक सरणी लेने के लिए एक ही फ़ंक्शन समायोजित किया गया है:

void assignZeros2(int(*arr)[4][5], const int dim1, const int dim2, const int dim3) {
    for (int i = 0; i < dim1; i++) {
        for (int j = 0; j < dim2; j++) {
            for (int k = 0; k < dim3; k++) {
                *(*(*(arr + i) + j) + k) = 0;
                // or equivalent assignment
                arr[i][j][k] = 0;
            }
        }
    }
}

आप कैसे उम्मीद करेंगे, यह तर्क के रूप में किसी भी 3 मंद सरणियों को ले सकता है जो कि दूसरे आयाम 4 तत्वों में और तीसरे आयाम 5 तत्वों में हैं। ऐसा कुछ भी ठीक होगा:

arr[1][4][5]
arr[2][4][5]
...
arr[10][4][5]
...

लेकिन हमें सभी आयामों को पहले एक तक निर्दिष्ट करना होगा।


5

C से ptr तक के प्राकृतिक प्रकार के क्षय के साथ C में मानक सरणी उपयोग

@ कोई व्यक्ति यहां अपने महान उत्तर में सही ढंग से बताता है :

पैरामीटर के रूप में एक सरणी पास करते समय, यह

void arraytest(int a[])

इसका मतलब बिल्कुल वैसा ही है

void arraytest(int *a)

हालाँकि, मुझे यह भी जोड़ने दें कि उपरोक्त दो रूप भी:

  1. मतलब बिल्कुल वैसा ही

     void arraytest(int a[0])
    
  2. जिसका अर्थ है बिल्कुल वैसा ही

     void arraytest(int a[1])
    
  3. जिसका अर्थ है बिल्कुल वैसा ही

     void arraytest(int a[2])
    
  4. जिसका अर्थ है बिल्कुल वैसा ही

     void arraytest(int a[1000])
    
  5. आदि।

ऊपर दिए गए सरणी उदाहरणों में से हर एक में, इनपुट पैरामीटर प्रकार एक के लिए तयint * हो जाता है, और बिना किसी चेतावनी और बिना किसी त्रुटि के कॉल किया जा सकता है, यहां तक ​​कि निर्मित विकल्पों के साथ भी -Wall -Wextra -Werrorचालू ( इन 3 बिल्ड विकल्पों पर विवरण के लिए मेरा रेपो देखें ), जैसे यह:

int array1[2];
int * array2 = array1;

// works fine because `array1` automatically decays from an array type
// to `int *`
arraytest(array1);
// works fine because `array2` is already an `int *` 
arraytest(array2);

तथ्य की बात के रूप में, "आकार" मूल्य ( [0], [1], [2], [1000], आदि) सरणी पैरामीटर के अंदर यहां सौंदर्य / स्व-प्रलेखन प्रयोजनों के लिए जाहिरा तौर पर सिर्फ है, और किसी भी पूर्णांक सकारात्मक हो सकता है ( size_tटाइप मुझे लगता है कि) आप चाहते हैं!

व्यवहार में, हालांकि, आपको इसका उपयोग उस सरणी के न्यूनतम आकार को निर्दिष्ट करने के लिए करना चाहिए जिसे आप फ़ंक्शन को प्राप्त करने की अपेक्षा करते हैं, ताकि कोड लिखते समय आपके लिए ट्रैक करना और सत्यापित करना आसान हो। मिश्रा सी 2012 मानक ( खरीद / £ 15.00 यहाँ डाउनलोड मानक के 236-पीजी 2012 संस्करण पीडीएफ ) अब तक राज्य (जोर जोड़ा) के रूप में चला जाता है:

नियम 17.5 एक सरणी प्रकार के लिए घोषित पैरामीटर के अनुरूप फ़ंक्शन तर्क में उपयुक्त तत्वों की संख्या होगी।

...

यदि किसी पैरामीटर को एक निर्दिष्ट आकार के साथ एक सरणी के रूप में घोषित किया जाता है, तो प्रत्येक फ़ंक्शन कॉल में संबंधित तर्क को उस ऑब्जेक्ट में इंगित करना चाहिए जिसमें कम से कम कई तत्व हैं, जैसे कि सरणी।

...

एक फ़ंक्शन पैरामीटर के लिए एक सरणी घोषणाकर्ता का उपयोग एक सूचक का उपयोग करने की तुलना में फ़ंक्शन इंटरफ़ेस को अधिक स्पष्ट रूप से निर्दिष्ट करता है। फ़ंक्शन द्वारा अपेक्षित तत्वों की न्यूनतम संख्या स्पष्ट रूप से बताई गई है, जबकि यह पॉइंटर के साथ संभव नहीं है।

दूसरे शब्दों में, वे स्पष्ट आकार प्रारूप का उपयोग करने की सलाह देते हैं, भले ही सी मानक तकनीकी रूप से इसे लागू नहीं करता है - यह कम से कम आपको डेवलपर के रूप में स्पष्ट करने में मदद करता है, और कोड का उपयोग करने वाले अन्य लोगों के लिए, फ़ंक्शन किस आकार की अपेक्षा कर रहा है। आप में पास होने के लिए


सी में सरणियों पर टाइप सुरक्षा को मजबूर करना

जैसा कि @Winger सेंडोन मेरे जवाब के नीचे एक टिप्पणी में बताते हैं, हम C को सरणी आकार के आधार पर एक सरणी प्रकार का इलाज करने के लिए बाध्य कर सकते हैं !

सबसे पहले, आपको यह समझना चाहिए कि मेरे उदाहरण में बस ऊपर, int array1[2];इस तरह का उपयोग करना : अपने आप में क्षय होने का arraytest(array1);कारण बनता array1है int *। फिर भी, यदि आप इसके बजाय पते लेते हैं array1और कॉल करते हैं arraytest(&array1), तो आपको पूरी तरह से अलग व्यवहार मिलता है! अब, यह एक में क्षय नहीं करता है int *! इसके बजाय, का प्रकार &array1है int (*)[2], जिसका अर्थ है "सूचक int के आकार 2 की एक सरणी के लिए" , या "सूचक प्रकार के आकार 2 की एक सरणी के लिए सूचक" । इसलिए, आप किसी सरणी पर टाइप सेफ्टी के लिए C को फोर्स कर सकते हैं, जैसे:

void arraytest(int (*a)[2])
{
    // my function here
}

यह सिंटैक्स पढ़ना मुश्किल है, लेकिन फ़ंक्शन पॉइंटर के समान है । ऑनलाइन टूल, cdecl , हमें बताता है कि int (*a)[2]इसका मतलब है: "int के सरणी 2 के रूप में एक पॉइंटर घोषित करें" (2 ints की सरणी के लिए पॉइंटर )। : कोष्टक के बिना संस्करण के साथ इस भ्रमित न करें int * a[2], जो साधन: "पूर्णांक के लिए सूचक की सरणी 2 के रूप में एक घोषित" (2 की सरणी संकेत करने के लिए int)।

अब, यह फ़ंक्शन आपको &इनपुट ऑपरेटर के रूप में इस तरह के पते ऑपरेटर ( ) के साथ कॉल करने की आवश्यकता देता है , जो कोर के आकार के एक पैरामीटर के रूप में एक इनपुट पैरामीटर का उपयोग कर रहा है !:

int array1[2];

// ok, since the type of `array1` is `int (*)[2]` (ptr to array of 
// 2 ints)
arraytest(&array1); // you must use the & operator here to prevent
                    // `array1` from otherwise automatically decaying
                    // into `int *`, which is the WRONG input type here!

यह, हालांकि, एक चेतावनी का उत्पादन करेगा:

int array1[2];

// WARNING! Wrong type since the type of `array1` decays to `int *`:
//      main.c:32:15: warning: passing argument 1 of ‘arraytest’ from 
//      incompatible pointer type [-Wincompatible-pointer-types]                                                            
//      main.c:22:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
arraytest(array1); // (missing & operator)

आप यहां इस कोड का परीक्षण कर सकते हैं

सी कंपाइलर को इस चेतावनी को एक त्रुटि में बदलने के लिए मजबूर करने के लिए, ताकि आप हमेशा arraytest(&array1);केवल सही आकार के इनपुट सरणी का उपयोग करके कॉल करें और ( int array1[2];इस मामले में), -Werrorअपने बिल्ड विकल्पों में जोड़ें । यदि onlinegdb.com पर ऊपर दिए गए टेस्ट कोड को चला रहे हैं, तो टॉप-राइट में गियर आइकन पर क्लिक करके यह विकल्प टाइप करने के लिए "अतिरिक्त कंपाइलर फ्लैग" पर क्लिक करें। अब, यह चेतावनी:

main.c:34:15: warning: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Wincompatible-pointer-types]                                                            
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’    

इस बिल्ड त्रुटि में बदल जाएगा:

main.c: In function ‘main’:
main.c:34:15: error: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Werror=incompatible-pointer-types]
     arraytest(array1); // warning!
               ^~~~~~
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
 void arraytest(int (*a)[2])
      ^~~~~~~~~
cc1: all warnings being treated as errors

ध्यान दें कि आप किसी दिए गए आकार के सरणियों के लिए "टाइप सेफ" पॉइंटर्स भी बना सकते हैं, जैसे:

int array[2];
// "type safe" ptr to array of size 2 of int:
int (*array_p)[2] = &array;

... लेकिन मैं यह जरूरी नहीं सुझाता , क्योंकि यह मुझे याद दिलाता है कि भाषा के वाक्यविन्यास जटिलता, क्रियात्मकता, और कठिनाई आर्किटेक्चरिंग कोड की असाधारण उच्च लागत पर, हर जगह टाइप सुरक्षा को मजबूर करने के लिए इस्तेमाल की जाने वाली C ++ हरकतों का एक बहुत कुछ है, और जो नापसंद है पहले के बारे में कई बार ranted है (उदाहरण के लिए: "C ++ पर मेरे विचार" यहां देखें )।


अतिरिक्त परीक्षणों और प्रयोग के लिए, नीचे दिए गए लिंक को भी देखें।

संदर्भ

ऊपर लिंक देखें। इसके अलावा:

  1. मेरा कोड प्रयोग ऑनलाइन: https://onlinegdb.com/B1RsrBDFD

2
void arraytest(int (*a)[1000])बेहतर है क्योंकि तब संकलक त्रुटि करेगा यदि आकार गलत है।
विंगर सेंडोन

@WingerSendon, मुझे पता था कि कुछ सूक्ष्मताएँ हैं जिन्हें मुझे यहाँ सत्यापित करने की आवश्यकता थी, और वह वाक्य रचना भ्रामक है (जैसे कि एक फ़ंक्शन ptr वाक्यविन्यास भ्रामक है), इसलिए मैंने अपना समय लिया और अंत में एक बड़े नए अनुभाग के साथ अपना उत्तर अपडेट किया Forcing type safety on arrays in C, जो आपके कवर कर रहा है बिंदु।
गेब्रियल स्टेपल्स

0

यदि आप उपयोग करते हैं a[]या *a: Arrays को हमेशा संदर्भ द्वारा पास किया जाता है :

int* printSquares(int a[], int size, int e[]) {   
    for(int i = 0; i < size; i++) {
        e[i] = i * i;
    }
    return e;
}

int* printSquares(int *a, int size, int e[]) {
    for(int i = 0; i < size; i++) {
        e[i] = i * i;
    }
    return e;
}

मैं इसे उभार रहा हूं। मुझे यकीन नहीं है कि यह नीचे क्यों है
गैब्रियल स्टेपल्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.