सी में एक समारोह से एक `संरचना` लौटें


171

आज मैं एक दो दोस्तों को सिखा रहा था कि कैसे सी का उपयोग करें struct। उनमें से एक ने पूछा कि क्या आप structकिसी फ़ंक्शन से लौट सकते हैं , जिसके लिए मैंने उत्तर दिया था: "नहीं! आप इसके बजाय गतिशील रूप से mallocएड करने के लिए संकेत लौटाएंगे struct।"

किसी ऐसे व्यक्ति से आ रहा है, जो मुख्य रूप से C ++ करता है, मैं उम्मीद कर रहा था कि मैं structमूल्यों के आधार पर वापसी नहीं कर पा रहा हूं । C ++ में आप operator =अपनी वस्तुओं के लिए ओवरलोड कर सकते हैं और अपने ऑब्जेक्ट को मान द्वारा वापस करने के लिए एक फ़ंक्शन का पूरा अर्थ रखते हैं। सी में, हालांकि, आपके पास वह विकल्प नहीं है और इसलिए मुझे यह सोच कर मिला कि वास्तव में कंपाइलर क्या कर रहा है। निम्नलिखित को धयान मे रखते हुए:

struct MyObj{
    double x, y;
};

struct MyObj foo(){
    struct MyObj a;

    a.x = 10;
    a.y = 10;

    return a;
}        

int main () {

    struct MyObj a;

    a = foo();    // This DOES work
    struct b = a; // This does not work

    return 0;
}    

मैं समझता हूं कि struct b = a;काम क्यों नहीं करना चाहिए - आप operator =अपने डेटा प्रकार के लिए अधिभार नहीं डाल सकते । यह कैसा है जो a = foo();ठीक संकलन करता है? क्या इसका मतलब कुछ और है struct b = a;? शायद सवाल पूछने के लिए है: क्या वास्तव returnमें =हस्ताक्षर करने के लिए संयोजन में बयान करता है?

[संपादित करें]: ठीक है, मुझे बताया गया था struct b = aकि एक वाक्यविन्यास त्रुटि है - यह सही है और मैं एक बेवकूफ हूँ! लेकिन इससे यह और भी जटिल हो जाता है! प्रयोग struct MyObj b = aवास्तव में काम करता है! मुझे यहां क्या समझ नहीं आ रहा है?


23
struct b = a;एक वाक्यविन्यास त्रुटि है। यदि आप प्रयास करें तो क्या होगा struct MyObj b = a;?
ग्रेग हेवगिल

2
@GregHewgill: आप बिल्कुल सही हैं। हालांकि, दिलचस्प struct MyObj b = a;बात यह है कि काम करने लगता है :)
mmirzadeh

जवाबों:


199

आप =किसी भी समस्या के बिना किसी फ़ंक्शन (या ऑपरेटर का उपयोग ) से एक संरचना वापस कर सकते हैं । यह भाषा का एक अच्छी तरह से परिभाषित हिस्सा है। केवल समस्या struct b = aयह है कि आपने एक पूर्ण प्रकार प्रदान नहीं किया है। struct MyObj b = aठीक काम करेगा। आप संरचनाओं को फ़ंक्शन के रूप में अच्छी तरह से पास कर सकते हैं - एक संरचना बिल्कुल वैसा ही है जैसा कि पैरामीटर पासिंग, रिटर्न मान और असाइनमेंट के प्रयोजनों के लिए कोई अंतर्निहित प्रकार।

यहां एक साधारण प्रदर्शन कार्यक्रम है जो तीनों करता है - एक संरचना को पैरामीटर के रूप में पारित करता है, एक फ़ंक्शन से एक संरचना लौटाता है, और असाइनमेंट स्टेटमेंट में संरचनाओं का उपयोग करता है:

#include <stdio.h>

struct a {
   int i;
};

struct a f(struct a x)
{
   struct a r = x;
   return r;
}

int main(void)
{
   struct a x = { 12 };
   struct a y = f(x);
   printf("%d\n", y.i);
   return 0;
}

अगला उदाहरण बहुत अधिक समान है, लेकिन intप्रदर्शन प्रयोजनों के लिए अंतर्निहित प्रकार का उपयोग करता है। दो कार्यक्रमों में पैरामीटर पासिंग, असाइनमेंट आदि के लिए पास-बाय-वैल्यू के संबंध में समान व्यवहार है।

#include <stdio.h>

int f(int x) 
{
  int r = x;
  return r;
}

int main(void)
{
  int x = 12;
  int y = f(x);
  printf("%d\n", y);
  return 0;
}

14
यह काफी दिलचस्प है। मैं हमेशा इस धारणा के अधीन था कि आपको इसके लिए संकेत की आवश्यकता है। मैं गलत था :)
mmirzadeh

8
आप निश्चित रूप से संकेत की जरूरत नहीं है । उस समय, अधिकांश समय आप उनका उपयोग करना चाहते हैं - निहित स्मृति प्रतियां जो मूल्य के आधार पर चारों ओर लहराती संरचनाएं लेती हैं, सीपीयू चक्रों का एक वास्तविक अपशिष्ट हो सकता है, मेमोरी बैंडविड्थ का उल्लेख नहीं करना।
कार्ल नॉरम

10
@CarlNorum एक संरचना को कितना बड़ा करना पड़ता है कि एक कॉपी की कीमत मॉलॉक + से अधिक होती है?
जोसेफ

7
@josefx, एक प्रति? शायद बहुत बड़ा। बात यह है कि आम तौर पर यदि आप मूल्य के आधार पर संरचनाओं को पास कर रहे हैं, तो आप उन्हें बहुत कॉपी कर रहे हैं । वैसे भी यह वास्तव में उतना आसान नहीं है। आप स्थानीय या वैश्विक संरचनाओं के आसपास से गुजर सकते हैं, इस मामले में थायर आवंटन लागत बहुत अधिक मुक्त है।
कार्ल नॉरम

7
आपको फ़ंक्शन बॉडी के बाहर लौटाए गए मान के लिए पॉइंटर्स और मेमोरी के आवंटन की आवश्यकता होती है जैसे ही किसी मूल्य के लिए आवंटित मेमोरी की मात्रा का अनुपालन समय पर नहीं किया जाता है। यह स्ट्रक्चर्स के लिए है, इसलिए C फ़ंक्शन को उन्हें वापस करने में कोई समस्या नहीं है।
रिस्टोरियरपोस्ट

33

इस तरह के रूप में एक कॉल करते समय a = foo();, कंपाइलर स्टैक पर परिणाम संरचना के पते को धक्का दे सकता है और इसे foo()फ़ंक्शन को "छिपे हुए" सूचक के रूप में पास करता है। प्रभावी रूप से, यह कुछ इस तरह बन सकता है:

void foo(MyObj *r) {
    struct MyObj a;
    // ...
    *r = a;
}

foo(&a);

हालांकि, इसका सटीक कार्यान्वयन संकलक और / या मंच पर निर्भर है। जैसा कि कार्ल नॉरम नोट करते हैं, यदि संरचना काफी छोटी है, तो इसे पूरी तरह से एक रजिस्टर में वापस पारित किया जा सकता है।


11
यह पूरी तरह से कार्यान्वयन पर निर्भर है। उदाहरण के लिए, आर्मक नियमित पैरामीटर पासिंग (या रिटर्न वैल्यू) रजिस्टरों में छोटे पर्याप्त ढांचे को पारित करेगा।
कार्ल नॉरम

क्या यह एक स्थानीय चर के लिए एक संकेतक नहीं लौटाएगा? लौटी संरचना के लिए मेमोरी fooस्टैक फ्रेम का हिस्सा नहीं हो सकती है । यह एक ऐसी जगह पर होना है जो की वापसी के अतीत से बचता है foo
एंडर्स एबेल

@AndersAbel: मुझे लगता है कि ग्रेग का मतलब क्या है कि कंपाइलर मुख्य फ़ंक्शन में चर के लिए एक पॉइंटर लेता है और इसे फ़ंक्शन में पास करता है foo। फ़ंक्शन के अंदर foo, आप बस असाइनमेंट करते हैं
mmirzadeh

4
@AndersAbel: *r = aअंत में (प्रभावी रूप से) कॉलर चर के लिए स्थानीय चर की एक प्रतिलिपि होगा। मैं कहता हूं कि "प्रभावी रूप से" क्योंकि संकलक आरवीओ को लागू कर सकता है और स्थानीय चर को aपूरी तरह से समाप्त कर सकता है ।
ग्रेग हेवगिल

3
हालांकि यह सीधे सवाल का जवाब नहीं देता है, यही कारण है कि बहुत से लोग Google के माध्यम से यहां आएंगे c return struct: वे जानते हैं कि सीडीसीएल eaxमें मूल्य द्वारा लौटा दिया गया है और सामान्य रूप से संरचनाएं अंदर फिट नहीं होती हैं eax। यह वही है जिसे मैं देख रहा था।
सिरो सेंटिल्ली 郝海东 冠状 iro 事件 法轮功 '

14

struct bक्योंकि यह एक सिंटैक्स त्रुटि है लाइन काम नहीं करता। यदि आप इसे उस प्रकार को शामिल करने के लिए विस्तारित करते हैं तो यह ठीक काम करेगा

struct MyObj b = a;  // Runs fine

यहाँ C क्या कर रहा है अनिवार्य रूप memcpyसे स्रोत संरचना से गंतव्य तक है। यह असाइनमेंट और structमानों की वापसी दोनों के लिए सही है (और वास्तव में C में हर दूसरे मूल्य)


+1, वास्तव में, कई कंपाइलर्स वास्तव memcpyमें इस मामले में शाब्दिक कॉल का उत्सर्जन करेंगे - कम से कम, यदि संरचना यथोचित रूप से बड़ी है।
कार्ल नॉरम

तो, एक डेटाटाइप के आरंभीकरण के दौरान, मेमसीपी फ़ंक्शन काम करता है ??
bhwansahni

1
@bhuwansahni मैं निश्चित नहीं हूँ कि आप यहाँ क्या पूछ रहे हैं। क्या आप थोड़ा विस्तार कर सकते हैं?
जारेदपार

4
@JaredPar - कंपाइलर अक्सर संरचना की स्थितियों के लिए फ़ंक्शन को कॉल करते हैं memcpy। आप एक त्वरित परीक्षण कार्यक्रम बना सकते हैं और उदाहरण के लिए GCC को देख सकते हैं। अंतर्निहित प्रकारों के लिए जो नहीं होगा - वे उस तरह के अनुकूलन को ट्रिगर करने के लिए पर्याप्त बड़े नहीं हैं।
कार्ल नॉरम

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

9

हां, यह संभव है कि हम संरचना और वापसी संरचना भी पारित कर सकते हैं। आप सही थे लेकिन आपने वास्तव में डेटा प्रकार को पारित नहीं किया था जो कि इस संरचना की तरह होना चाहिए MyObj b = a।

वास्तव में मुझे यह भी पता चला कि जब मैं सूचक या वैश्विक चर का उपयोग किए बिना फ़ंक्शन के लिए एक से अधिक मूल्यों को वापस करने के लिए एक बेहतर समाधान खोजने की कोशिश कर रहा था

अब नीचे उसी के लिए उदाहरण दिया गया है, जो औसत के बारे में एक छात्र के अंकों के विचलन की गणना करता है।

#include<stdio.h>
struct marks{
    int maths;
    int physics;
    int chem;
};

struct marks deviation(struct marks student1 , struct marks student2 );

int main(){

    struct marks student;
    student.maths= 87;
    student.chem = 67;
    student.physics=96;

    struct marks avg;
    avg.maths= 55;
    avg.chem = 45;
    avg.physics=34;
    //struct marks dev;
    struct marks dev= deviation(student, avg );
    printf("%d %d %d" ,dev.maths,dev.chem,dev.physics);

    return 0;
 }

struct marks deviation(struct marks student , struct marks student2 ){
    struct marks dev;

    dev.maths = student.maths-student2.maths;
    dev.chem = student.chem-student2.chem;
    dev.physics = student.physics-student2.physics; 

    return dev;
}

5

जहां तक ​​मुझे याद है, सी के पहले संस्करणों ने केवल एक मान वापस करने की अनुमति दी थी जो एक प्रोसेसर रजिस्टर में फिट हो सकता है, जिसका अर्थ है कि आप केवल एक संकेतक को एक संरचना में वापस कर सकते हैं। वही तर्क कार्य तर्कों पर लागू होता है।

अधिक हाल के संस्करण बड़े डेटा ऑब्जेक्ट्स जैसे स्ट्रक्चर्स के आसपास से गुजरने की अनुमति देते हैं। मुझे लगता है कि यह सुविधा अस्सी या नब्बे के दशक के शुरुआती दिनों में आम थी।

हालांकि, ऐरे को अभी भी पारित किया जा सकता है और केवल संकेत के रूप में वापस किया जा सकता है।


यदि आप किसी संरचना के अंदर रखते हैं, तो आप किसी सरणी को मान से वापस कर सकते हैं। आप जिस मूल्य से नहीं लौट सकते हैं वह एक चर-लंबाई सरणी है।
हन

1
हां, मैं एक संरचना के अंदर एक सरणी रख सकता हूं, लेकिन मैं उदाहरण के लिए टाइपडीएफ चार गिरफ्तारी नहीं लिख सकता हूं arr foo () {...} एक सरणी लौटाया नहीं जा सकता, भले ही आकार ज्ञात हो।
जियोर्जियो

क्या डाउनवॉटर डाउनवोट का कारण बता सकता है? यदि मेरे जवाब में गलत जानकारी है, तो मुझे इसे ठीक करने में खुशी होगी।
जियोर्जियो

4

आप सी में संरचना असाइन कर सकते हैं a = b;वैध वाक्यविन्यास है।

आप बस उस प्रकार के हिस्से को छोड़ देते हैं - जो आपकी टैग लाइन में - काम नहीं करता है।


4

एक संरचना को वापस पारित करने में कोई समस्या नहीं है। इसे मूल्य से पारित किया जाएगा

लेकिन, क्या होगा यदि संरचना में कोई भी सदस्य होता है जिसके पास स्थानीय चर का पता होता है

struct emp {
    int id;
    char *name;
};

struct emp get() {
    char *name = "John";

    struct emp e1 = {100, name};

    return (e1);
}

int main() {

    struct emp e2 = get();

    printf("%s\n", e2.name);
}

अब, यहाँ e1.name में फ़ंक्शन पाने के लिए एक मेमोरी एड्रेस लोकल () है। एक बार () रिटर्न मिलने के बाद, नाम के लिए स्थानीय पते को मुक्त कर दिया जाता। एसओ, फोन करने वाले में यदि हम उस पते पर पहुंचने का प्रयास करते हैं, तो इससे विभाजन दोष हो सकता है, क्योंकि हम एक मुक्त पते की कोशिश कर रहे हैं। वह बुरा है..

जहाँ e1.id पूरी तरह से मान्य होगा क्योंकि इसका मान e2.id पर कॉपी किया जाएगा

इसलिए, हमें हमेशा किसी फ़ंक्शन के स्थानीय मेमोरी पते को वापस करने से बचने की कोशिश करनी चाहिए।

किसी भी चीज को जब चाहें तब वापस किया जा सकता है


2
struct emp {
    int id;
    char *name;
};

struct emp get() {
    char *name = "John";

    struct emp e1 = {100, name};

    return (e1);
}

int main() {

    struct emp e2 = get();

    printf("%s\n", e2.name);
}

संकलक के नए संस्करणों के साथ ठीक काम करता है। आईडी की तरह, नाम की सामग्री असाइन किए गए संरचना चर पर कॉपी हो जाती है।


1
और भी सरल: संरचनात्मक साम्राज्य मिलता है () {वापसी {100, "जॉन"}; }
क्रिस रीड

1

स्ट्रक्चरल ई 2 एड्रेस को आर्गन के रूप में कैली स्टैक के लिए धकेल दिया जाता है और मान वहां असाइन हो जाते हैं। वास्तव में, eax reg में e2 का पता मिलता है। यह कॉल बाय रेफरेंस की तरह काम करता है।

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