2009-07-09 24 views
56

मैं सी में संदर्भ द्वारा structs की एक सरणी कैसे पारित कर सकते हैं?सी में संदर्भ द्वारा एक सरणी पास?

एक उदाहरण के रूप:

struct Coordinate { 
    int X; 
    int Y; 
}; 
SomeMethod(Coordinate *Coordinates[]){ 
    //Do Something with the array 
} 
int main(){ 
    Coordinate Coordinates[10]; 
    SomeMethod(&Coordinates); 
} 

उत्तर

115

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

void reset(int *array, int size) { 
    memset(array,0,size * sizeof(*array)); 
} 
int main() 
{ 
    int array[10]; 
    reset(array, 10); // sets all elements to 0 
} 

अब, क्या आप चाहते हैं सरणी ही बदल रहा है अगर (तत्वों की संख्या ...) आप इसे ढेर या वैश्विक सरणियों के साथ ऐसा नहीं कर सकते, केवल ढेर में गतिशील रूप से आबंटित स्मृति के साथ। प्रश्न संपादित करें आप structs की एक सरणी से गुजर के बारे में विशेष रूप से पूछना में

void resize(int **p, int size) { 
    free(*p); 
    *p = malloc(size * sizeof(int)); 
} 
int main() { 
    int *p = malloc(10 * sizeof(int)); 
    resize(&p, 20); 
} 

: उस मामले में, आप सूचक को बदलना चाहते हैं आप एक सूचक यह करने के लिए गुजरना होगा। तुम वहाँ दो समाधान है: एक typedef घोषित, या स्पष्ट है कि आप एक struct से गुजर रहे हैं बनाने:

struct Coordinate { 
    int x; 
    int y; 
}; 
void f(struct Coordinate coordinates[], int size); 
typedef struct Coordinate Coordinate; // generate a type alias 'Coordinate' that is equivalent to struct Coordinate 
void g(Coordinate coordinates[], int size); // uses typedef'ed Coordinate 

आप के रूप में आप यह घोषणा प्रकार typedef कर सकते हैं (और यह सी में एक आम मुहावरा है):

typedef struct Coordinate { 
    int x; 
    int y; 
} Coordinate; 
+1

मेमसेट (सरणी, 0, आकार का सरणी) का उपयोग करें; रीसेट के अंदर() चक्र के बजाय :) – rodi

+4

@rodi: यह एक दिलचस्प बात है, क्योंकि आपने एक बग पेश किया है :) मैंने कोड को 'memset' का उपयोग करने के लिए अद्यतन किया है क्योंकि इसका उपयोग किया जाना चाहिए:' memset (array, 0, आकार * आकार * सरणी) '-' आकार (सरणी) 'इस मामले में एक * सूचक * का आकार है, न कि बिंदु डेटा। –

+0

यह अच्छी प्रैक्टिस ** ** नहीं है ** मॉलोक के रिटर्न वैल्यू को टाइप करें। [यहां] देखें (https: // stackoverflow।कॉम/प्रश्न/605845/डू-आई-कास्ट-द-परिणाम-ऑफ-मॉलोक) – Aka

8

सी भाषा किसी भी प्रकार की संदर्भ द्वारा पारित समर्थन नहीं करता। निकटतम समकक्ष प्रकार के लिए एक सूचक को पास करना है।

यहाँ दोनों भाषाओं

में एक काल्पनिक उदाहरण है C++ शैली एपीआई

void UpdateValue(int& i) { 
    i = 42; 
} 

निकटतम सी बराबर

void UpdateValue(int *i) { 
    *i = 42; 
} 
+0

मुझे पता है, लेकिन मुझे एक ही विधि के साथ एक सरणी पारित करने में समस्या मिलती है: s –

+3

@Yassir, क्या आप एक उदाहरण दे सकते हैं? – JaredPar

+0

यह उदाहरण संदर्भ द्वारा एक आदिम प्रकार को पारित करने का प्रदर्शन करता है (एक सूचक जो मूल्य से गुजरता है) का उपयोग करता है लेकिन नीचे दिए गए पद में सही तरीके से प्रदर्शित संदर्भ के अनुसार structs की सरणी को पारित करने का प्रदर्शन नहीं करता है। –

6

सादा सी में आप अपने एपीआई में एक पॉइंटर/आकार संयोजन का उपयोग कर सकते हैं।

void doSomething(MyStruct* mystruct, size_t numElements) 
{ 
    for (size_t i = 0; i < numElements; ++i) 
    { 
     MyStruct current = mystruct[i]; 
     handleElement(current); 
    } 
} 

संकेत का उपयोग उपलब्ध-कॉल दर-संदर्भ निकटतम करने के लिए सी

में
6

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

//this is bad 
char* getname() 
{ 
    char name[100]; 
    return name; 
} 

//this is better 
char* getname() 
{ 
    char *name = malloc(100); 
    return name; 
    //remember to free(name) 
} 
5

Arrays डिफ़ॉल्ट रूप से डिफ़ॉल्ट रूप से संदर्भ द्वारा पारित कर दिए जाते हैं। वास्तव में पहले तत्व को सूचक का मूल्य पारित किया जाता है। इसलिए यह प्राप्त करने वाला फ़ंक्शन या विधि सरणी में मानों को संशोधित कर सकता है।

void SomeMethod(Coordinate Coordinates[]){Coordinates[0].x++;}; 
int main(){ 
    Coordinate tenCoordinates[10]; 
    tenCoordinates[0].x=0; 
    SomeMethod(tenCoordinates[]); 
    SomeMethod(&tenCoordinates[0]); 
    if(0==tenCoordinates[0].x - 2;){ 
    exit(0); 
    } 
    exit(-1); 
} 

दो कॉल समकक्ष हैं, और निकास मूल्य 0 होना चाहिए;

9

यहां कुछ उत्तरों पर थोड़ा सा विस्तार करने के लिए ...

सी में, जब किसी सरणी पहचानकर्ता को & या आकार के ऑपरेटर के अलावा किसी संदर्भ में दिखाई देता है, तो पहचानकर्ता का प्रकार पूरी तरह से "टी-एन-एलिमेंट सरणी" से "पॉइंटर टू टी" में परिवर्तित हो जाता है, और इसका मान सरणी में पहले तत्व के पते पर निश्चित रूप से सेट किया गया है (जो सरणी के पते के समान ही है)। यही कारण है कि जब आप किसी फ़ंक्शन के लिए तर्क के रूप में सरणी पहचानकर्ता को पास करते हैं, तो फ़ंक्शन को सरणी के बजाय बेस प्रकार के लिए पॉइंटर प्राप्त होता है। चूंकि आप यह नहीं बता सकते कि पहले तत्व को पॉइंटर को देखकर सरणी कितनी बड़ी है, आपको आकार को एक अलग पैरामीटर के रूप में पास करना होगा।

struct Coordinate { int x; int y; }; 
void SomeMethod(struct Coordinate *coordinates, size_t numCoordinates) 
{ 
    ... 
    coordinates[i].x = ...; 
    coordinates[i].y = ...; 
    ... 
} 
int main (void) 
{ 
    struct Coordinate coordinates[10]; 
    ... 
    SomeMethod (coordinates, sizeof coordinates/sizeof *coordinates); 
    ... 
} 

कार्यों के लिए सरणी पास करने के कुछ वैकल्पिक तरीके हैं।

टी की एक सरणी के सूचक के रूप में ऐसी कोई बात नहीं है, के रूप में टी करने के लिए एक सूचक के विपरीत, आप इस तरह के एक सूचक घोषणा करेंगे के रूप में

T (*p)[N]; 

इस मामले में, पी एक करने के लिए एक सूचक है टी की एन-एलिमेंट सरणी (टी * पी [एन] के विपरीत, जहां पी टी के सूचक का एन-एलिमेंट सरणी है)। तो अगर आप सरणी के सूचक के रूप पहला तत्व के लिए सूचक का विरोध करने दे सकते हैं:

struct Coordinate { int x; int y }; 

void SomeMethod(struct Coordinate (*coordinates)[10]) 
{ 
    ... 
    (*coordinates)[i].x = ...; 
    (*coordinates)[i].y = ...; 
    ... 
} 

int main(void) 
{ 
    struct Coordinate coordinates[10]; 
    ... 
    SomeMethod(&coordinates); 
    ... 
} 

इस विधि का नुकसान यह है कि सरणी आकार तय हो गई है, टी के एक 10 तत्व सरणी के लिए सूचक के बाद से है

struct Coordinate { int x; int y; }; 
struct CoordinateWrapper { struct Coordinate coordinates[10]; }; 
void SomeMethod(struct CoordinateWrapper wrapper) 
{ 
    ... 
    wrapper.coordinates[i].x = ...; 
    wrapper.coordinates[i].y = ...; 
    ... 
} 
int main(void) 
{ 
    struct CoordinateWrapper wrapper; 
    ... 
    SomeMethod(wrapper); 
    ... 
} 

इस विधि का लाभ यह है कि आप नहीं कर रहे हैं है: टी

एक तीसरा विधि एक struct में सरणी रैप करने के लिए है की एक 20-तत्व वाली सरणी के लिए सूचक से एक अलग प्रकार का है पॉइंटर्स के साथ चारों ओर mucking। नुकसान यह है कि सरणी का आकार निश्चित है (फिर से, टी का 10-तत्व सरणी टी के 20-तत्व सरणी से एक अलग प्रकार है)।

+0

आपके रैपर फॉर्मूलेशन का एक और नुकसान यह है कि 'wrapper' _value_ द्वारा पारित किया जा रहा है। 80 बाइट्स (-श), और 'SomeMethod()' के अंदर असाइनमेंट का 'मुख्य' में घोषित 'रैपर' पर कोई प्रभाव नहीं पड़ता है। एक बग की तरह लग रहा है। – bobbogo

2

अरे दोस्तों यहाँ एक साधारण परीक्षण कार्यक्रम है जो दिखाता है कि कैसे नए या मॉलोक का उपयोग करके सरणी आवंटित और पास किया जाए। बस कट, पेस्ट और इसे चलाएं। मज़े करो!

struct Coordinate 
{ 
    int x,y; 
}; 

void resize(int **p, int size) 
{ 
    free(*p); 
    *p = (int*) malloc(size * sizeof(int)); 
} 

void resizeCoord(struct Coordinate **p, int size) 
{ 
    free(*p); 
    *p = (Coordinate*) malloc(size * sizeof(Coordinate)); 
} 

void resizeCoordWithNew(struct Coordinate **p, int size) 
{ 
    delete [] *p; 
    *p = (struct Coordinate*) new struct Coordinate[size]; 
} 

void SomeMethod(Coordinate Coordinates[]) 
{ 
    Coordinates[0].x++; 
    Coordinates[0].y = 6; 
} 

void SomeOtherMethod(Coordinate Coordinates[], int size) 
{ 
    for (int i=0; i<size; i++) 
    { 
     Coordinates[i].x = i; 
     Coordinates[i].y = i*2; 
    } 
} 

int main() 
{ 
    //static array 
    Coordinate tenCoordinates[10]; 
    tenCoordinates[0].x=0; 
    SomeMethod(tenCoordinates); 
    SomeMethod(&(tenCoordinates[0])); 
    if(tenCoordinates[0].x - 2 == 0) 
    { 
     printf("test1 coord change successful\n"); 
    } 
    else 
    { 
     printf("test1 coord change unsuccessful\n"); 
    } 


    //dynamic int 
    int *p = (int*) malloc(10 * sizeof(int)); 
    resize(&p, 20); 

    //dynamic struct with malloc 
    int myresize = 20; 
    int initSize = 10; 
    struct Coordinate *pcoord = (struct Coordinate*) malloc (initSize * sizeof(struct Coordinate)); 
    resizeCoord(&pcoord, myresize); 
    SomeOtherMethod(pcoord, myresize); 
    bool pass = true; 
    for (int i=0; i<myresize; i++) 
    { 
     if (! ((pcoord[i].x == i) && (pcoord[i].y == i*2))) 
     {   
      printf("Error dynamic Coord struct [%d] failed with (%d,%d)\n",i,pcoord[i].x,pcoord[i].y); 
      pass = false; 
     } 
    } 
    if (pass) 
    { 
     printf("test2 coords for dynamic struct allocated with malloc worked correctly\n"); 
    } 


    //dynamic struct with new 
    myresize = 20; 
    initSize = 10; 
    struct Coordinate *pcoord2 = (struct Coordinate*) new struct Coordinate[initSize]; 
    resizeCoordWithNew(&pcoord2, myresize); 
    SomeOtherMethod(pcoord2, myresize); 
    pass = true; 
    for (int i=0; i<myresize; i++) 
    { 
     if (! ((pcoord2[i].x == i) && (pcoord2[i].y == i*2))) 
     {   
      printf("Error dynamic Coord struct [%d] failed with (%d,%d)\n",i,pcoord2[i].x,pcoord2[i].y); 
      pass = false; 
     } 
    } 
    if (pass) 
    { 
     printf("test3 coords for dynamic struct with new worked correctly\n"); 
    } 


    return 0; 
} 
संबंधित मुद्दे