क्या मुझे पैरामीटर के माध्यम से या रिटर्न वैल्यू द्वारा सी स्ट्रक्चर्स को इनिशियलाइज़ करना चाहिए? [बन्द है]


33

जिस कंपनी में मैं काम करता हूं, वह अपने सभी डेटा स्ट्रक्चर्स को इनिशियलाइज़ फंक्शन के जरिए इनिशियलाइज़ कर रही है, जैसे:

//the structure
typedef struct{
  int a,b,c;  
} Foo;

//the initialize function
InitializeFoo(Foo* const foo){
   foo->a = x; //derived here based on other data
   foo->b = y; //derived here based on other data
   foo->c = z; //derived here based on other data
}

//initializing the structure  
Foo foo;
InitializeFoo(&foo);

मैंने अपनी स्ट्रक्चर्स को इस तरह इनिशियलाइज़ करने की कोशिश में कुछ धक्का दिया है:

//the structure
typedef struct{
  int a,b,c;  
} Foo;

//the initialize function
Foo ConstructFoo(int a, int b, int c){
   Foo foo;
   foo.a = a; //part of parameter input (inputs derived outside of function)
   foo.b = b; //part of parameter input (inputs derived outside of function)
   foo.c = c; //part of parameter input (inputs derived outside of function)
   return foo;
}

//initialize (or construct) the structure
Foo foo = ConstructFoo(x,y,z);

क्या एक दूसरे के ऊपर एक फायदा है?
मुझे कौन सा करना चाहिए, और मैं इसे बेहतर अभ्यास के रूप में कैसे सही ठहराऊंगा?


4
@gnat यह संरचना आरंभीकरण के बारे में एक स्पष्ट सवाल है। यह धागा कुछ उसी तर्क का प्रतीक है जिसे मैं इस विशेष डिजाइन निर्णय के लिए लागू देखना चाहता हूं।
ट्रेवर हिक्की

2
@ जेफ्री हम सी में हैं, इसलिए हमारे पास वास्तव में तरीके नहीं हैं। यह हमेशा मूल्यों का प्रत्यक्ष सेट नहीं होता है। कभी-कभी एक संरचना को शुरू करना मूल्यों (किसी तरह) को प्राप्त करना है, और संरचना को शुरू करने के लिए कुछ तर्क करता है।
ट्रेवर हिक्की

1
@JacquesB मुझे मिला "हर घटक जो आप बनाते हैं, वह दूसरों की तुलना में अलग होगा। संरचना के लिए कहीं और एक प्रारंभिक () फ़ंक्शन का उपयोग किया जाता है। तकनीकी रूप से बोलने वाला, इसे निर्माता कहना भ्रामक है।"
ट्रेवर हिक्की

1
@TrevorHickey InitializeFoo()एक कंस्ट्रक्टर है। C ++ कंस्ट्रक्टर से एकमात्र अंतर यह है कि thisसूचक स्पष्ट रूप से अंतर्निहित रूप से पारित किया गया है। InitializeFoo()और इसी C ++ Foo::Foo()का संकलित कोड बिल्कुल समान है।
सेमीस्टर

2
बेहतर विकल्प: C ++ के C का उपयोग करना बंद करें। Autowin।
थॉमस एडिंग जूल

जवाबों:


25

दूसरे दृष्टिकोण में आपके पास कभी भी आधा प्रारंभिक फू नहीं होगा। सभी निर्माण को एक जगह पर रखना एक अधिक समझदार और स्पष्ट स्थान लगता है।

लेकिन ... 1 रास्ता इतना बुरा नहीं है, और अक्सर कई क्षेत्रों में उपयोग किया जाता है (यहां तक ​​कि निर्भरता-इंजेक्शन के लिए सबसे अच्छे तरीके की चर्चा भी है, या तो आपके 1 तरह से संपत्ति-इंजेक्शन, या 2 की तरह निर्माता इंजेक्शन) । न गलत है।

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


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

4
@TrevorHickey: वास्तव में मैं कहूंगा कि आपके द्वारा दिए गए उदाहरणों के बीच दो प्रमुख अंतर हैं - (1) एक फ़ंक्शन को संरचना को प्रारंभ करने के लिए एक संकेतक दिया जाता है, और दूसरे में यह एक प्रारंभिक संरचना देता है; (2) एक में आरंभीकरण पैरामीटर को फंक्शन में पास किया जाता है, और दूसरे में वे निहित होते हैं। आप (2) के बारे में अधिक पूछ रहे हैं, लेकिन यहाँ उत्तर (1) पर ध्यान केंद्रित कर रहे हैं। आप स्पष्ट करना चाह सकते हैं कि - मुझे संदेह है कि ज्यादातर लोग स्पष्ट मापदंडों और एक सूचक का उपयोग करते हुए दो के एक हाइब्रिड की सिफारिश करेंगे:void SetupFoo(Foo *out, int a, int b, int c)
भजन

1
पहला दृष्टिकोण "अर्ध-आरंभिक" Fooसंरचना की ओर कैसे ले जाएगा ? पहला दृष्टिकोण भी एक ही स्थान पर सभी आरंभीकरण करता है। (या आप एक विचार कर रहे हैं संयुक्त राष्ट्र प्रारंभ Foostruct होना करने के लिए "आधा प्रारंभ"?)
jamesdlin

1
@jamesdlin ऐसे मामलों में जहां फू बनाया जाता है, और इनिशियलीफू गलती से छूट जाता है। यह केवल एक लंबा विवरण टाइप किए बिना 2-चरण के आरंभीकरण का वर्णन करने के लिए भाषण का एक आंकड़ा था। मुझे लगा कि अनुभवी डेवलपर प्रकार के लोग समझेंगे।
gbjbaanb

22

दोनों दृष्टिकोण एकल फ़ंक्शन कॉल में इनिशियलाइज़ेशन कोड को बंडल करते हैं। अब तक सब ठीक है।

हालांकि, दूसरे दृष्टिकोण के साथ दो मुद्दे हैं:

  1. दूसरा वाला वास्तव में परिणामी वस्तु का निर्माण नहीं करता है, यह स्टैक पर किसी अन्य ऑब्जेक्ट को इनिशियलाइज़ करता है, जिसे बाद में अंतिम ऑब्जेक्ट पर कॉपी किया जाता है। यही कारण है कि मैं दूसरे दृष्टिकोण को थोड़ा हीनता के रूप में देखूंगा। आपके द्वारा प्राप्त पुश-बैक इस एक्सट्रॉनिक कॉपी के कारण होने की संभावना है।

    यह तब और भी बुरा होता है जब आप किसी वर्ग Derivedको प्राप्त करते हैं Foo(C में ऑब्जेक्ट ओरिएंटेशन के लिए बड़े पैमाने पर उपयोग किया जाता है): दूसरे दृष्टिकोण के साथ, फ़ंक्शन ConstructDerived()कॉल करेगा ConstructFoo(), परिणामस्वरूप अस्थायी Fooऑब्जेक्ट को किसी Derivedऑब्जेक्ट के सुपरक्लास स्लॉट में कॉपी करेगा ; Derivedवस्तु का आरंभीकरण समाप्त करना ; केवल परिणामी वस्तु को फिर से वापसी पर कॉपी करने के लिए। एक तीसरी परत जोड़ें, और पूरी चीज पूरी तरह से हास्यास्पद हो जाती है।

  2. दूसरे दृष्टिकोण के साथ, ConstructClass()कार्यों में निर्माणाधीन वस्तु के पते तक पहुंच नहीं है। इससे निर्माण के दौरान वस्तुओं को लिंक करना असंभव हो जाता है, क्योंकि जब किसी वस्तु को कॉलबैक के लिए किसी अन्य ऑब्जेक्ट के साथ खुद को पंजीकृत करने की आवश्यकता होती है, तो इसकी आवश्यकता होती है।


अंत में, सभी structsपूरी तरह से विकसित वर्ग नहीं हैं। कुछ structsप्रभावी रूप से बस इन चरों के मूल्यों पर किसी भी आंतरिक प्रतिबंध के बिना, एक साथ चर का एक गुच्छा बंडल करते हैं। typedef struct Point { int x, y; } Point;इसका एक अच्छा उदाहरण होगा। इन के लिए एक आरंभिक कार्य का उपयोग ओवरकिल लगता है। इन मामलों में, यौगिक शाब्दिक सिंटैक्स सुविधाजनक हो सकता है (यह C99 है):

Point = { .x = 7, .y = 9 };

या

Point foo(...) {
    //other stuff

    return (Point){ .x = n, .y = n*n };
}

5
मुझे नहीं लगा कि प्रतियां en.wikipedia.org/wiki/Copy_elision
ट्रेवर हिक्की

5
कंपाइलर कॉपी को खत्म कर सकता है, इस तथ्य को कम नहीं करता है कि आपने कॉपी को लिख दिया है। सी में, शानदार संचालन लिखना और उन्हें ठीक करने के लिए संकलक पर निर्भर होना बुरा स्टाइल माना जाता है। यह C ++ से अलग है जहां लोग गर्व करते हैं जब वे यह साबित कर सकते हैं कि संकलक सैद्धांतिक रूप से उनके नासिका द्वारा छोड़े गए सभी cruft को हटा सकता है। सी में, लोग ठीक उसी कोड को लिखने की कोशिश करते हैं जिसे मशीन को निष्पादित करना चाहिए। वैसे भी, दुर्गम पतों के बारे में बात बनी हुई है, कॉपी इलनेस वहां आपकी मदद नहीं कर सकता।
सेमीस्टर

3
कंपाइलर का उपयोग करने वाले किसी को भी यह अपेक्षा करनी चाहिए कि वे जो कोड लिखते हैं वह कंपाइलर द्वारा रूपांतरित किया जाए। जब तक वे एक हार्डवेयर सी दुभाषिया नहीं चला रहे हैं, तब तक वे जो कोड लिखते हैं, वे उस कोड को निष्पादित नहीं करेंगे, भले ही उन्हें अन्यथा विश्वास करना आसान हो। यदि वे अपने संकलक को नहीं समझते हैं, तो वे समझ में आएंगे, और यह बाइनरी में int x = 3;स्ट्रिंग xको संग्रहीत न करने से अलग है । पता और विरासत अंक अच्छे हैं; उत्थान की असफलता मूर्खता है।
याकूब

@Yakk: ऐतिहासिक रूप से, C का आविष्कार सिस्टम प्रोग्रामिंग के लिए उच्च-स्तरीय असेंबली भाषा के रूप में सेवा करने के लिए किया गया था। पिछले वर्षों में, इसकी पहचान तेजी से बढ़ती जा रही है। कुछ लोग चाहते हैं कि यह एक अनुकूलित अनुप्रयोग भाषा हो, लेकिन चूंकि उच्च-स्तरीय असेंबली भाषा का कोई बेहतर रूप सामने नहीं आया है, इसलिए सी को अभी भी इस बाद की भूमिका निभाने की जरूरत है। मैं इस विचार के साथ कुछ भी गलत नहीं देखता कि एक अच्छी तरह से लिखा प्रोग्राम कोड कम से कम शालीनता के साथ संकलित होने पर भी कम से कम शालीनता से व्यवहार करना चाहिए, हालांकि यह सुनिश्चित करने के लिए कि वास्तव में काम करने के लिए सी को कुछ चीजों को जोड़ना होगा जिनकी इसमें लंबे समय से कमी है।
सुपरकैट

@ याक: निर्देशन के लिए, उदाहरण के लिए, जो एक संकलक को बताएगा "निम्नलिखित चर को कोड के निम्नलिखित खिंचाव के दौरान रजिस्टरों में सुरक्षित रूप से रखा जा सकता है" और साथ ही ब्लॉक-कॉपी करने के एक साधन के अलावा एक प्रकार की unsigned charअनुमति देता है जिसके लिए अनुकूलन की अनुमति होगी सख्त अलियासिंग नियम अपर्याप्त होगा, साथ ही साथ प्रोग्रामर अपेक्षाओं को स्पष्ट करता है।
सुपरकाट

1

संरचना की सामग्री और उपयोग किए जा रहे विशेष संकलक के आधार पर, या तो दृष्टिकोण तेज हो सकता है। एक विशिष्ट पैटर्न यह है कि कुछ मानदंडों को पूरा करने वाली संरचनाएं रजिस्टरों में वापस आ सकती हैं; अन्य संरचना प्रकारों को लौटाने वाले कार्यों के लिए कॉलर को अस्थायी संरचना के लिए जगह आवंटित करने की आवश्यकता होती है, (आमतौर पर स्टैक पर) और इसके पते को "छिपे हुए" पैरामीटर के रूप में पास किया जाता है; ऐसे मामलों में जहां किसी फ़ंक्शन की वापसी को सीधे स्थानीय चर में संग्रहीत किया जाता है, जिसका पता किसी भी बाहरी कोड द्वारा नहीं होता है, कुछ कंपाइलर सीधे उस चर के पते को पारित करने में सक्षम हो सकते हैं।

यदि कोई संरचना प्रकार रजिस्टरों में लौटाए जाने के लिए किसी विशेष कार्यान्वयन की आवश्यकताओं को पूरा करता है (जैसे कि या तो एक मशीन शब्द से बड़ा नहीं है, या ठीक दो मशीन शब्दों को भरना) तो किसी फ़ंक्शन को वापस करने से संरचना संरचना के पते से गुजरने की तुलना में तेज़ हो सकती है, विशेष रूप से बाहरी चर के लिए एक चर के पते को उजागर करने के बाद से जो इसकी एक प्रति रख सकता है, कुछ उपयोगी अनुकूलन को रोक सकता है। यदि कोई प्रकार ऐसी आवश्यकताओं को पूरा नहीं करता है, तो एक फ़ंक्शन के लिए उत्पन्न कोड जो एक संरचना को लौटाता है, एक फ़ंक्शन पॉइंटर संकेतक को स्वीकार करने वाले फ़ंक्शन के समान होगा; कॉलिंग कोड संभवतः उस फॉर्म के लिए तेज़ होगा जो एक पॉइंटर लेता है, लेकिन यह फॉर्म कुछ अनुकूलन अवसरों को खो देता है।

यह बहुत बुरा है C यह कहने का साधन प्रदान नहीं करता है कि किसी फ़ंक्शन को पास-इन पॉइंटर की प्रतिलिपि (सी + + संदर्भ के समान शब्दार्थ) रखने से मना किया गया है क्योंकि इस तरह के प्रतिबंधित पॉइंटर को पास करने से पासिंग के प्रत्यक्ष प्रदर्शन लाभ प्राप्त होंगे एक पूर्व-मौजूदा ऑब्जेक्ट के लिए एक पॉइंटर, लेकिन एक ही समय में एक चर का पता लगाने पर विचार करने के लिए एक कंपाइलर की आवधिक लागतों से बचें "उजागर"।


3
आपके अंतिम बिंदु पर: किसी फ़ंक्शन को संदर्भ के रूप में पास किए गए पॉइंटर की प्रतिलिपि रखने से फ़ंक्शन को रोकने के लिए C ++ में कुछ भी नहीं है, फ़ंक्शन बस ऑब्जेक्ट का पता ले सकता है। किसी अन्य ऑब्जेक्ट का निर्माण करने के लिए संदर्भ का उपयोग करने के लिए भी स्वतंत्र है जिसमें वह संदर्भ शामिल है (कोई नग्न सूचक नहीं बनाया गया)। फिर भी, पॉइंटर कॉपी या ऑब्जेक्ट में संदर्भ ऑब्जेक्ट को इंगित कर सकता है जो वे इंगित करते हैं, जिससे झूलने वाला पॉइंटर / संदर्भ बनता है। तो संदर्भ-सुरक्षा के बारे में बात बहुत मूक है।
सेमी जस्टर

@cmaster: प्लेटफ़ॉर्म पर जो एक संकेतक को अस्थायी भंडारण के लिए पास करके संरचनाएं लौटाते हैं, सी कंपाइलर उस भंडारण के पते तक पहुंचने के किसी भी तरीके से कार्य नहीं करते हैं। C ++ में, संदर्भ द्वारा पारित चर का पता प्राप्त करना संभव है, लेकिन जब तक कॉलर पास किए गए आइटम के जीवनकाल की गारंटी नहीं देता है (जिस स्थिति में यह आमतौर पर एक पॉइंटर पारित होता है) अपरिभाषित व्यवहार की संभावना होगी।
सुपरकैट

1

"आउटपुट-पैरामीटर" शैली के पक्ष में एक तर्क यह है कि यह फ़ंक्शन को एक त्रुटि कोड वापस करने की अनुमति देता है।

struct MyStruct {
    int x;
    char *y;
    // ...
};

int MyStruct_init(struct MyStruct *out) {
    // ...
    char *c = malloc(n);
    if (!c) {
        return -1;
    }
    out->y = c;
    return 0;  // Success!
}

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


1
हालांकि एक सेट कर सकता है errno
डेडुप्लिकेटर

0

मैं मान रहा हूं कि आपका ध्यान आउटपुट पैरामीटर बनाम रिटर्न के माध्यम से आरंभीकरण पर है, न कि निर्माण तर्क में आपूर्ति की विसंगति।

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


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