C का उपयोग करके एक सरणी लौटाता है


153

मैं C के लिए अपेक्षाकृत नया हूं और मुझे सरणियों से निपटने के तरीकों में कुछ मदद चाहिए। जावा प्रोग्रामिंग से आ रहा है, मुझे int [] method()एक सरणी वापस करने के लिए कहने में सक्षम होने के लिए उपयोग किया जाता है । हालांकि, मुझे पता चला है कि सी के साथ आपको एरे के लिए पॉइंटर्स का उपयोग करना होगा जब आप उन्हें वापस करते हैं। एक नया प्रोग्रामर होने के नाते, मुझे वास्तव में यह बिल्कुल भी समझ में नहीं आता है, यहां तक ​​कि कई मंचों के माध्यम से मैंने देखा है।

मूल रूप से, मैं एक विधि लिखने की कोशिश कर रहा हूं जो सी में एक चार सरणी देता है। मैं एक सरणी के साथ विधि प्रदान करूंगा (इसे वापसी कहते हैं)। यह पिछले सरणी से एक नया सरणी बनाएगा और इसे एक पॉइंटर लौटाएगा। इसे शुरू करने के लिए मुझे बस कुछ मदद की ज़रूरत है और एक बार सरणी से बाहर भेजे जाने पर पॉइंटर को कैसे पढ़ें। यह समझाने में कोई मदद की सराहना की है।

सरणी रिटर्निंग फ़ंक्शन के लिए प्रस्तावित कोड प्रारूप

char *returnArray(char array []){
 char returned [10];
 //methods to pull values from array, interpret them, and then create new array
 return &(returned[0]); //is this correct?
} 

फंक्शन का कॉलर

int main(){
 int i=0;
 char array []={1,0,0,0,0,1,1};
 char arrayCount=0;
 char* returnedArray = returnArray(&arrayCount); ///is this correct?
 for (i=0; i<10;i++)
  printf(%d, ",", returnedArray[i]);  //is this correctly formatted?
}

मैंने अभी तक इसका परीक्षण नहीं किया है क्योंकि मेरा सी कंपाइलर फिलहाल काम नहीं कर रहा है लेकिन मैं इसका पता लगाना चाहूंगा


क्या रिटर्न सरणी एक ज्ञात आकार है जैसा आपके कोड नमूने में दर्शाया गया है? उत्तर में वर्णित स्टैक मुद्दों के अलावा मैं केवल अन्य गोच को देखता हूं कि यदि आपका रिटर्न ऐरे एक अनिश्चित आकार है, तो सी में जिस तरह से पॉइंटर्स / एरेज़ काम करते हैं, आपको पता नहीं होगा कि यह कितना बड़ा है।
अजीब बात है

हां, मैं हर समय इनकमिंग ऐरे के आकार को जानता हूं। इनपुट और आउटपुट ऐरे का आकार न बदले।
user1506919

1
सी लैंग्वेज का विकास * - bell-labs.com/usr/dmr/www/chist.html
x4444

जवाबों:


225

आप सी। में फ़ंक्शन से सरणियाँ नहीं लौटा सकते। आप भी ऐसा नहीं कर सकते (नहीं):

char *returnArray(char array []){
 char returned [10];
 //methods to pull values from array, interpret them, and then create new array
 return &(returned[0]); //is this correct?
} 

returned स्वत: भंडारण अवधि के साथ बनाया गया है और जब यह फ़ंक्शन लौटता है, तो यह अमान्य घोषित हो जाएगा, जब फ़ंक्शन वापस आ जाएगा।

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

विकल्प 1:

फ़ंक्शन के अंदर डायनामिक रूप से मेमोरी आवंटित करें (डीलर के लिए जिम्मेदार कॉलर ret)

char *foo(int count) {
    char *ret = malloc(count);
    if(!ret)
        return NULL;

    for(int i = 0; i < count; ++i) 
        ret[i] = i;

    return ret;
}

इसे ऐसे कॉल करें:

int main() {
    char *p = foo(10);
    if(p) {
        // do stuff with p
        free(p);
    }

    return 0;
}

विकल्प 2:

फोन करने वाले द्वारा दिया गया एक उपदेशित बफ़र भरें (कॉलर आवंटित करता है bufऔर फ़ंक्शन को पास करता है)

void foo(char *buf, int count) {
    for(int i = 0; i < count; ++i)
        buf[i] = i;
}

और इसे कॉल करें:

int main() {
    char arr[10] = {0};
    foo(arr, 10);
    // No need to deallocate because we allocated 
    // arr with automatic storage duration.
    // If we had dynamically allocated it
    // (i.e. malloc or some variant) then we 
    // would need to call free(arr)
}

33
विकल्प 3: (एक स्थिर सरणी)
moooeeeep

5
@mooeeeeeep: हां, मैंने चीजों को सरल रखने के लिए जानबूझकर छोड़ दिया, लेकिन हां, आप फ़ंक्शन के भीतर से घोषित किए गए स्थिर डेटा के लिए एक पॉइंटर लौटा सकते हैं।
एड एस।

3
@ user1506919: मैं वास्तव में विकल्प 2 को पसंद करूंगा क्योंकि यह स्पष्ट है कि कौन मेमोरी आवंटित करता है और डील करता है, लेकिन मैं आपके लिए एक उदाहरण जोड़ूंगा।
एड एस।

7
विकल्प 4: ऐसी संरचना लौटाएं जिसमें एक निश्चित आकार का सरणी हो।
टॉड लेहमैन

2
विकल्प 5: एक संघ को लौटाएँ जिसमें एक निश्चित आकार की सरणी हो।
sqr163

27

C का सरणियों का उपचार जावा से बहुत अलग है, और आपको उसी के अनुसार अपनी सोच को समायोजित करना होगा। C में Arrays प्रथम श्रेणी की वस्तुएँ नहीं हैं (अर्थात, एक सरणी व्यंजक इसे अधिकांश संदर्भों में "array-ness" नहीं बनाए रखता है)। सी में, टाइप "एन-एलिमेंट एरे T" की अभिव्यक्ति को "सिचुएशन टू टाइप" के एक एक्सप्रेशन में बदल दिया जाएगा T, सिवाय इसके कि एरे एक्सप्रेशन sizeofया अनरी &ऑपरेटर्स का ऑपरेंड है या नहीं तो एरे एक्सप्रेशन एक स्ट्रिंग शाब्दिक है जिसका उपयोग घोषणा में दूसरे ऐरे को इनिशियलाइज़ करने के लिए किया जाता है।

अन्य बातों के अलावा, इसका मतलब है कि आप किसी फ़ंक्शन के लिए एक एक्सप्रेशन एक्सप्रेशन पास नहीं कर सकते हैं और इसे एरे टाइप के रूप में प्राप्त किया है ; फ़ंक्शन वास्तव में एक पॉइंटर प्रकार प्राप्त करता है:

void foo(char *a, size_t asize)
{
  // do something with a
}

int bar(void)
{
  char str[6] = "Hello";
  foo(str, sizeof str);
}

कॉल करने के लिए foo, अभिव्यक्ति strको प्रकार से परिवर्तित किया char [6]जाता है char *, यही वजह है कि इसके बजाय पहले पैरामीटर fooको घोषित किया char *aजाता है char a[6]। में sizeof str, के बाद से सरणी अभिव्यक्ति का एक संकार्य है sizeofऑपरेटर, यह एक सूचक प्रकार के लिए परिवर्तित नहीं कर रहा है तो आप सरणी (6) में बाइट की संख्या मिलता है।

यदि आप वास्तव में रुचि रखते हैं, तो आप डेनिस रिची की द डेवलपमेंट ऑफ द सी लैंग्वेज यह समझने के लिए पढ़ सकते हैं कि यह उपचार कहां से आता है।

यह है कि कार्य सरणी प्रकारों को वापस नहीं कर सकता है, जो ठीक है क्योंकि सरणी अभिव्यक्तियाँ असाइनमेंट का लक्ष्य नहीं हो सकती हैं।

कॉल करने वाले के लिए सबसे सुरक्षित तरीका एरे को परिभाषित करना है, और इसके पते और आकार को उस फ़ंक्शन को पास करना है जो इसे लिखना है:

void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize)
{
  ...
  dstArray[i] = some_value_derived_from(srcArray[i]);
  ...
}

int main(void)
{
  char src[] = "This is a test";
  char dst[sizeof src];
  ...
  returnArray(src, sizeof src, dst, sizeof dst);
  ...
}

फ़ंक्शन को गतिशील रूप से आवंटित करने और सूचक और आकार को वापस करने के लिए एक अन्य विधि है:

char *returnArray(const char *srcArray, size_t srcSize, size_t *dstSize)
{
  char *dstArray = malloc(srcSize);
  if (dstArray)
  {
    *dstSize = srcSize;
    ...
  }
  return dstArray;
}

int main(void)
{
  char src[] = "This is a test";
  char *dst;
  size_t dstSize;

  dst = returnArray(src, sizeof src, &dstSize);
  ...
  free(dst);
  ...
}

इस मामले में, कॉलर freeलाइब्रेरी फ़ंक्शन के साथ सरणी से निपटने के लिए जिम्मेदार है।

ध्यान दें कि dstउपरोक्त कोड में एक सरल सूचक है char, न कि किसी सूचक को सरणी के लिए char। सी के पॉइंटर और एरे सेमेंटिक्स ऐसे हैं कि आप सबस्क्रिप्ट ऑपरेटर []को या तो एरे टाइप या पॉइंटर टाइप का एक्सप्रेशन दे सकते हैं ; दोनों src[i]और dst[i]का उपयोग करेंगे i'सरणी के वें तत्व (भले ही केवल srcसरणी प्रकार है)।

आप एक एन-एलिमेंट एरे को पॉइंटर घोषित कर सकते हैंT और कुछ ऐसा ही कर सकते हैं:

char (*returnArray(const char *srcArr, size_t srcSize))[SOME_SIZE]
{
  char (*dstArr)[SOME_SIZE] = malloc(sizeof *dstArr);
  if (dstArr)
  {
    ...
    (*dstArr)[i] = ...;
    ...
  }
  return dstArr;
}

int main(void)
{
  char src[] = "This is a test";
  char (*dst)[SOME_SIZE];
  ...
  dst = returnArray(src, sizeof src);
  ...
  printf("%c", (*dst)[j]);
  ...
}

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


2
"सी का विकास" से आपका लिंक टूट गया है ... ऐसा लग रहा है कि हमें यहाँ निर्देशित करना चाहिए: bell-labs.com/usr/dmr/www/chist.html
Dr.Queso

@Kundor: क्या पुनरावृत्ति barएक सूचक है, सरणी नहीं। एक फ़ंक्शन पैरामीटर घोषणा के संदर्भ में, T a[N]और T a[]दोनों के रूप में माना जाता है T *a
जॉन बोडे

@ जॉनबोड: आप सही कह रहे हैं! किसी कारण से मैंने सोचा था कि स्टैक पर निश्चित आकार के सरणियों को पारित किया गया था। मुझे एक अवसर याद आता है, कई साल पहले, जब मैंने पाया कि एक सरणी का आकार पैरामीटर हस्ताक्षर में निर्दिष्ट किया जाना था, लेकिन मुझे भ्रमित होना चाहिए था।
निक मैटेओ

@ जॉनबोड, दूसरे कोड भाग पहली पंक्ति में: void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize)अंतिम पैरामीटर size_tप्रकार में नहीं होना चाहिए char
सेफी

11

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

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    typedef
    struct 
    {
        char v[10];
    } CHAR_ARRAY;



    CHAR_ARRAY returnArray(CHAR_ARRAY array_in, int size)
    {
        CHAR_ARRAY returned;

        /*
        . . . methods to pull values from array, interpret them, and then create new array
        */

        for (int i = 0;  i < size; i++ )
            returned.v[i] = array_in.v[i] + 1;

        return returned; // Works!
    } 




    int main(int argc, char * argv[])
    {
        CHAR_ARRAY array = {1,0,0,0,0,1,1};

        char arrayCount = 7;

        CHAR_ARRAY returnedArray = returnArray(array, arrayCount); 

        for (int i = 0; i < arrayCount; i++)
            printf("%d, ", returnedArray.v[i]);  //is this correctly formatted?

        getchar();
        return 0;
    }

मैं इस तकनीक की ताकत और कमजोरियों पर टिप्पणी आमंत्रित करता हूं। मैंने ऐसा करने की जहमत नहीं उठाई।


1
स्पष्ट नहीं है कि यह स्वीकृत उत्तर क्यों नहीं है। यह सवाल नहीं था कि क्या किसी व्यूह में सूचक को लौटाना संभव है।
फ्रैंक पक

क्या मेमोरी CHAR_ARRAY returnedको ढेर पर आवंटित किया गया है? यह निश्चित रूप से स्टैक पर नहीं हो सकता है ( returnArray()दाएं के स्टैक फ्रेम में ?
मिन्ह ट्रान

9

इस स्वादिष्ट बुराई कार्यान्वयन के बारे में कैसे?

array.h

#define IMPORT_ARRAY(TYPE)    \
    \
struct TYPE##Array {    \
    TYPE* contents;    \
    size_t size;    \
};    \
    \
struct TYPE##Array new_##TYPE##Array() {    \
    struct TYPE##Array a;    \
    a.contents = NULL;    \
    a.size = 0;    \
    return a;    \
}    \
    \
void array_add(struct TYPE##Array* o, TYPE value) {    \
    TYPE* a = malloc((o->size + 1) * sizeof(TYPE));    \
    TYPE i;    \
    for(i = 0; i < o->size; ++i) {    \
        a[i] = o->contents[i];    \
    }    \
    ++(o->size);    \
    a[o->size - 1] = value;    \
    free(o->contents);    \
    o->contents = a;    \
}    \
void array_destroy(struct TYPE##Array* o) {    \
    free(o->contents);    \
}    \
TYPE* array_begin(struct TYPE##Array* o) {    \
    return o->contents;    \
}    \
TYPE* array_end(struct TYPE##Array* o) {    \
    return o->contents + o->size;    \
}

main.c

#include <stdlib.h>
#include "array.h"

IMPORT_ARRAY(int);

struct intArray return_an_array() {
    struct intArray a;
    a = new_intArray();
    array_add(&a, 1);
    array_add(&a, 2);
    array_add(&a, 3);
    return a;
}

int main() {
    struct intArray a;
    int* it;
    int* begin;
    int* end;
    a = return_an_array();
    begin = array_begin(&a);
    end = array_end(&a);
    for(it = begin; it != end; ++it) {
        printf("%d ", *it);
    }
    array_destroy(&a);
    getchar();
    return 0;
}

2
यह मेरी जिज्ञासा पैदा करने के लिए काफी स्वादिष्ट है। क्या आप थोड़ा और समझा सकते हैं कि आपने वहां क्या किया था या शायद इस स्वादिष्टता को पढ़ने का सुझाव देते हैं जिसे आप कहते हैं? अग्रिम में धन्यवाद।
अनहिलिग

1
@Unheilig - ध्यान दें कि इसमें संभावित संभावित बग हैं, यह सिर्फ कॉन्सेप्ट का एक सबूत था। उस ने कहा, चाल structएक सरणी कंटेनर / वस्तु के रूप में लौट रही है । इसे C ++ std :: वेक्टर की तरह समझें। प्रीप्रोसेसर intइस के संस्करण का विस्तार करेगा struct intArray { int* contents; int size; };
पीरोसैपेड

1
मुझे अप्रोच पसंद है। प्रो: यह सामान्य समाधान है; साँस लेना: स्मृति गहन समाधान। कोन आकार के वैक्टर के लिए इष्टतम नहीं है। वैसे भी यह inital size आवंटन के साथ उन्नत किया जा सकता है। मैं निश्चित रूप से कुछ आवंटन जांच जोड़ूंगा। बहुत अच्छा प्रस्ताव :)
urkon

ऑब्जेक्ट ओरिएंटेड-एस्क प्रीपॉसिंगिंग मिक्स-मैश। मुझें यह पसंद है।
जैक गिफिन

6

आपके मामले में, आप स्टैक पर एक ऐरे बना रहे हैं और एक बार जब आप फंक्शन स्कोप छोड़ देते हैं, तो एरे को आच्छादित कर दिया जाएगा। इसके बजाय, एक गतिशील रूप से आवंटित सरणी बनाएं और इसे एक सूचक लौटाएं।

char * returnArray(char *arr, int size) {
    char *new_arr = malloc(sizeof(char) * size);
    for(int i = 0; i < size; ++i) {
        new_arr[i] = arr[i];
    }
    return new_arr;
}

int main() {

    char arr[7]= {1,0,0,0,0,1,1};
    char *new_arr = returnArray(arr, 7);

    // don't forget to free the memory after you're done with the array
    free(new_arr);

}

2
newC. में कोई ऑपरेटर नहीं है। वह C ++ है।
एरिक पोस्टपिसिल

1
और होने sizeof(char)की गारंटी है 1, इसलिए इस मामले में आप उस बिट को छोड़ सकते हैं malloc
एड एस।

ठीक है, अगर मैं नए एरे की सामग्री का प्रिंट आउट लेना चाहता था, तो क्या मैं अपना 'प्रिंटफ' बयान दे सकता था, लेकिन 'एरे' के साथ 'रिटर्नअरे' को बदल सकता था?
user1506919

आप फ़ंक्शन को ठीक से नहीं बुला रहे हैं (केवल एक तर्क जब हस्ताक्षर के लिए दो की आवश्यकता होती है)।
एड एस।

आप अंदर जा रहे हैं &arr। आप arrएक होना चाहते हैं char *, और इसे उपयोग करने में पास करें arr
क्रिस

4

आप इसे यहां बताए गए अन्य उत्तरों की तरह हीप मेमोरी ( मॉलोक () इनवोकेशन के माध्यम से ) का उपयोग कर सकते हैं , लेकिन आपको हमेशा मेमोरी का उपयोग करना चाहिए ( हर बार जब आप अपने फ़ंक्शन को कॉल करते हैं तो मुफ्त) का उपयोग करें । आप इसे एक स्थिर सरणी के साथ भी कर सकते हैं:

char* returnArrayPointer() 
{
static char array[SIZE];

// do something in your array here

return array; 
}

आप स्मृति प्रबंधन के बारे में चिंता किए बिना इसका उपयोग कर सकते हैं।

int main() 
{
char* myArray = returnArrayPointer();
/* use your array here */
/* don't worry to free memory here */
}

इस उदाहरण में आपको सरणी जीवनकाल में स्थैतिक कीवर्ड का उपयोग करना होगा ताकि वह जीवन भर के लिए आवेदन कर सके, इसलिए यह रिटर्न स्टेटमेंट के बाद नष्ट नहीं होगा। बेशक, इस तरह से आप पूरे एप्लिकेशन जीवन के लिए अपनी मेमोरी में SIZE बाइट्स पर कब्जा कर लेते हैं, इसलिए इसे ठीक से आकार दें!


2

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

void returnArray(int size, char *retArray)
{
  // work directly with retArray or memcpy into it from elsewhere like
  // memcpy(retArray, localArray, size); 
}

#define ARRAY_SIZE 20

int main(void)
{
  char foo[ARRAY_SIZE];
  returnArray(ARRAY_SIZE, foo);
}

0

आप इस तरह कोड का उपयोग कर सकते हैं:

char *MyFunction(some arguments...)
{
    char *pointer = malloc(size for the new array);
    if (!pointer)
        An error occurred, abort or do something about the error.
    return pointer; // Return address of memory to the caller.
}

जब आप ऐसा करते हैं, तो मेमोरी को बाद में मुक्त करने के लिए पते को पास करके मुक्त किया जाना चाहिए।

अन्य विकल्प हैं। एक रूटीन पॉइंटर एक सरणी (या एक एरे के हिस्से) में वापस आ सकता है जो कुछ मौजूदा संरचना का हिस्सा है। कॉल करने वाला एक सरणी पास कर सकता है, और एक नई सरणी के लिए स्थान आवंटित करने के बजाय, दिनचर्या केवल सरणी में लिखता है।

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