C संरचना में डिफ़ॉल्ट मान


92

मेरे पास इस तरह की एक डेटा संरचना है:

    संरचना फू {
        इंट आईडी;
        इंट मार्ग;
        int backup_route;
        int current_route;
    }

और एक फ़ंक्शन जिसे अपडेट () कहा जाता है, जिसका उपयोग इसमें परिवर्तन का अनुरोध करने के लिए किया जाता है।

  अद्यतन (42, NOT_care, NOT_care, new_route);

यह वास्तव में लंबा है और अगर मैं संरचना में कुछ जोड़ता हूं तो मुझे अद्यतन करने के लिए एक 'NOT_care' को हर किसी को जोड़ना होगा (...)।

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

    संरचना फू बार = {.id = 42,। current_route = new_route};
    अद्यतन (और बार);

अपडेट फ़ंक्शन को व्यक्त करने के लिए केवल जानकारी को पारित करने का सबसे सुरुचिपूर्ण तरीका क्या है?

और मैं चाहता हूं कि सब कुछ डिफ़ॉल्ट -1 हो ('केयर केयर' के लिए गुप्त कोड)

जवाबों:


155

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

const struct foo FOO_DONT_CARE = { // or maybe FOO_DEFAULT or something
    dont_care, dont_care, dont_care, dont_care
};
...
struct foo bar = FOO_DONT_CARE;
bar.id = 42;
bar.current_route = new_route;
update(&bar);

इस कोड का अप्रत्यक्ष रूप से समझने का कोई मानसिक ओवरहेड नहीं है, और यह बहुत स्पष्ट है कि barआप कौन से फ़ील्ड स्पष्ट रूप से सेट करते हैं (सुरक्षित रूप से) जिन्हें आप सेट नहीं करते हैं।


6
इस दृष्टिकोण का एक और बोनस यह है कि यह काम करने के लिए C99 सुविधाओं पर निर्भर नहीं करता है।
डी। शेवले

24
जब मैं इस परियोजना के लिए "बाहर गिर गया" 500 लाइनों को बदल दिया। काश मैं इस पर दो बार मतदान कर सकता था!
आर्थर उल्फेल्ट

4
PTHREAD_MUTEX_INITIALIZERयह भी उपयोग करता है।
yingted

मैंने संरचना में मौजूद तत्वों की संख्या पर लचीला होने के लिए एक्स-मैक्रोज़ पर भरोसा करने के नीचे एक उत्तर जोड़ा है ।
एंटोनियो

15

आप अपने गुप्त विशेष मान को 0 में बदल सकते हैं, और सी के डिफ़ॉल्ट संरचना-सदस्य शब्दार्थ का फायदा उठा सकते हैं

struct foo bar = { .id = 42, .current_route = new_route };
update(&bar);

इसके बाद इनिशियलाइज़र में अनिर्दिष्ट बार के सदस्यों के रूप में 0 पास करेंगे।

या आप एक मैक्रो बना सकते हैं जो आपके लिए डिफ़ॉल्ट आरंभीकरण करेगा:

#define FOO_INIT(...) { .id = -1, .current_route = -1, .quux = -1, ## __VA_ARGS__ }

struct foo bar = FOO_INIT( .id = 42, .current_route = new_route );
update(&bar);

क्या FOO_INIT काम करता है? कंपाइलर को शिकायत करनी चाहिए, मुझे लगता है, अगर आप एक ही सदस्य को दो बार इनिशियलाइज़ करते हैं।
जोनाथन लेफ़लर

1
मैंने इसे gcc के साथ आज़माया है और इसने शिकायत नहीं की है। इसके अलावा, मुझे इसके मानक में कुछ भी नहीं मिला है, वास्तव में, एक उदाहरण है जहां ओवरराइटिंग का विशेष रूप से उल्लेख किया गया है।
jpalecek

2
C99: open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf : 6.7.8.19: इनिशियलाइज़र सूची क्रम में आ जाएगी, प्रत्येक इनिशियलाइज़र किसी विशेष सबजेक्ट के लिए प्रदान किया जाता है, जो उसी के लिए पहले से सूचीबद्ध इनिशियलाइज़र को ओवरराइड करता है। subobject;
18

2
यदि यह सत्य है, तो क्या आप DEFAULT_FOO को परिभाषित नहीं कर सकते, जिसमें सभी आरंभीकरण हैं, और फिर स्ट्रक्चर फू बार = {DEFAULT_FOO, .id = 10}; ?
जॉन

7

<stdarg.h>आपको वैरिएड फ़ंक्शंस को परिभाषित करने की अनुमति देता है (जो अनिश्चित तर्कों को स्वीकार करता है, जैसे printf())। मैं एक फ़ंक्शन को परिभाषित करता हूं जो मनमानी संख्या में जोड़े के तर्क लेता है, एक जो संपत्ति को अद्यतन करने के लिए निर्दिष्ट करता है, और एक जो मूल्य निर्दिष्ट करता है। enumसंपत्ति का नाम निर्दिष्ट करने के लिए एक या एक स्ट्रिंग का उपयोग करें ।


हाय @Matt, क्षेत्र में enumचर कैसे मैप करें struct? यह हार्डकोड? तब यह फ़ंक्शन केवल विशिष्ट पर लागू होगा struct
प्रातः

5

शायद इसके बजाय प्रीप्रोसेसर मैक्रो परिभाषा का उपयोग करने पर विचार करें:

#define UPDATE_ID(instance, id)  ({ (instance)->id= (id); })
#define UPDATE_ROUTE(instance, route)  ({ (instance)->route = (route); })
#define UPDATE_BACKUP_ROUTE(instance, route)  ({ (instance)->backup_route = (route); })
#define UPDATE_CURRENT_ROUTE(instance, route)  ({ (instance)->current_route = (route); })

यदि आपका (उदाहरण foo) का उदाहरण वैश्विक है, तो आपको इसके लिए पैरामीटर की आवश्यकता नहीं है। लेकिन मैं मान रहा हूं कि आपके पास शायद एक से अधिक उदाहरण हैं। ({...}) ब्लॉक का प्रयोग एक GNU-ism है जो GCC पर लागू होता है; यह ब्लॉक के रूप में लाइनों को एक साथ रखने का एक अच्छा (सुरक्षित) तरीका है। अगर आपको बाद में मैक्रोज़ को और अधिक जोड़ने की आवश्यकता है, जैसे कि रेंज सत्यापन जाँच, तो आपको चीजों को तोड़ने के बारे में चिंता करने की ज़रूरत नहीं होगी जैसे कि / और कथनों और इसके आगे।

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


4

एक पैटर्न gobject का उपयोग एक वैरिएड फ़ंक्शन और प्रत्येक प्रॉपर्टी के लिए एन्यूमरेटेड मान है। इंटरफ़ेस कुछ इस तरह दिखता है:

update (ID, 1,
        BACKUP_ROUTE, 4,
        -1); /* -1 terminates the parameter list */

एक varargs फ़ंक्शन लिखना आसान है - http://www.eskimo.com/~scs/cclass/int/sx11b.html देखें । बस कुंजी -> मूल्य जोड़े से मेल खाते हैं और उचित संरचना विशेषताओं को सेट करते हैं।


4

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

जैसे


#define set_current_route(id, route) update(id, dont_care, dont_care, route)
#define set_route(id, route) update(id, dont_care, route, dont_care)
#define set_backup_route(id, route) update(id, route, dont_care, dont_care)

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


कुछ मामलों में एक ही कॉल में 3 बदल जाएंगे।
आर्थर उल्फेल्ट

आपके पास यह क्रम हो सकता है: set_current_rout (आईडी, मार्ग); set_route (आईडी, मार्ग); apply_change (आईडी); या इसी के समान। मुझे लगता है कि आप हर सेटर के लिए एक फ़ंक्शन कॉल कर सकते हैं। और वास्तविक गणना (या कभी भी) बाद के बिंदु पर करें।
क्विनमर

4

कैसे कुछ के बारे में:

struct foo bar;
update(init_id(42, init_dont_care(&bar)));

साथ में:

struct foo* init_dont_care(struct foo* bar) {
  bar->id = dont_care;
  bar->route = dont_care;
  bar->backup_route = dont_care;
  bar->current_route = dont_care;
  return bar;
}

तथा:

struct foo* init_id(int id, struct foo* bar) {
  bar->id = id;
  return bar;
}

और इसके बाद:

struct foo* init_route(int route, struct foo* bar);
struct foo* init_backup_route(int backup_route, struct foo* bar);
struct foo* init_current_route(int current_route, struct foo* bar);

C ++ में, एक समान पैटर्न का एक नाम है जो मुझे अभी याद नहीं है।

संपादित करें : इसे नामित पैरामीटर मुहावरा कहा जाता है ।


मेरा मानना ​​है कि इसका निर्माता कहा जाता है।
अज्ञात

नहीं, मेरा मतलब था कि आप ऐसा कर सकते हैं: अपडेट (bar.init_id (42) .init_rout_ (5) .init_backup_route (66));
रियुनेन

: यह "श्रृंखलित स्मालटाक शैली प्रारंभ" कहा जाने, यहां कम से कम लगता है cct.lsu.edu/~rguidry/ecl31docs/api/org/eclipse/ui/internal/...
Reunanen

2

मैं संरचनाओं के साथ जंग खा रहा हूं, इसलिए मैं शायद यहां कुछ कीवर्ड गायब कर रहा हूं। लेकिन शुरू में चूक के साथ एक वैश्विक संरचना के साथ शुरू क्यों नहीं, इसे अपने स्थानीय चर में कॉपी करें, फिर इसे संशोधित करें?

एक शुरुआती की तरह:

void init_struct( structType * s )
{
   memcopy(s,&defaultValues,sizeof(structType));
}

फिर जब आप इसका उपयोग करना चाहें:

structType foo;
init_struct( &foo ); // get defaults
foo.fieldICareAbout = 1; // modify fields
update( &foo ); // pass to function

2

आप X-Macro के साथ समस्या का समाधान कर सकते हैं

आप अपनी संरचना की परिभाषा को इसमें बदल देंगे:

#define LIST_OF_foo_MEMBERS \
    X(int,id) \
    X(int,route) \
    X(int,backup_route) \
    X(int,current_route)


#define X(type,name) type name;
struct foo {
    LIST_OF_foo_MEMBERS 
};
#undef X

और फिर आप आसानी से एक लचीले फ़ंक्शन को परिभाषित करने में सक्षम होंगे जो सभी क्षेत्रों को सेट करता है dont_care

#define X(type,name) in->name = dont_care;    
void setFooToDontCare(struct foo* in) {
    LIST_OF_foo_MEMBERS 
}
#undef X

यहां चर्चा के बाद , कोई डिफ़ॉल्ट मान को इस तरह भी परिभाषित कर सकता है:

#define X(name) dont_care,
const struct foo foo_DONT_CARE = { LIST_OF_STRUCT_MEMBERS_foo };
#undef X

जो इसमें अनुवाद करता है:

const struct foo foo_DONT_CARE = {dont_care, dont_care, dont_care, dont_care,};

और इसे hlovdal उत्तर के रूप में उपयोग करें , इस लाभ के साथ कि यहां रखरखाव आसान है, अर्थात foo_DONT_CAREसंरचनात्मक सदस्यों की संख्या को बदलना स्वचालित रूप से अपडेट हो जाएगा । ध्यान दें कि अंतिम "सहज" अल्पविराम स्वीकार्य है

मैंने पहली बार एक्स-मैक्रोज़ की अवधारणा को सीखा जब मुझे इस समस्या का समाधान करना था

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

यदि आप एक सभी intसंरचना के साथ ठीक हैं , तो आप डेटा प्रकार को छोड़ सकते हैं LIST_OF_foo_MEMBERSऔर बस संरचनात्मक परिभाषा के एक्स फ़ंक्शन को बदल सकते हैं#define X(name) int name;


यह तकनीक इस बात का फायदा उठाती है कि सी प्रीप्रोसेसर देर से बाइंडिंग का उपयोग करता है और यह तब बहुत उपयोगी हो सकता है जब आप किसी एक स्थान को किसी चीज़ को परिभाषित करने के लिए चाहते हैं (उदाहरण के लिए राज्यों में) और फिर 100% सुनिश्चित करें कि हर जगह जब इस्तेमाल की गई वस्तुएं हमेशा एक ही क्रम में हों, तो नए आइटम स्वचालित रूप से जोड़े और हटाए गए आइटम हटा दिए जाते हैं। मैं बहुत कम नामों का उपयोग करने का शौकीन नहीं हूं, Xऔर आम तौर पर _ENTRYसूची के नाम के लिए अपील करता हूं , जैसे #define LIST_SOMETHING LIST_SOMETHING_ENTRY(a1, a2, aN) \ LIST_SOMETHING_ENTRY(b1, b2, bN) ...
२०१ at में हवलदार

1

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

struct foo* bar = get_foo_ptr();
foo_ref.id = 42;
foo_ref.current_route = new_route;

या आप, जैसे कि पुक्कू ने सुझाव दिया है, संरचना के प्रत्येक क्षेत्र के लिए अलग-अलग पहुंच कार्य बना सकते हैं।

अन्यथा मैं जिस बेहतरीन समाधान के बारे में सोच सकता हूं, वह एक संरचनात्मक क्षेत्र में '0' के मान को 'अपडेट न करें' फ्लैग के रूप में मान रहा है - इसलिए आप केवल शून्य आउट स्ट्रक्चर को वापस करने के लिए एक funciton बनाते हैं, और फिर अपडेट करने के लिए इसका उपयोग करते हैं।

struct foo empty_foo(void)
{
    struct foo bar;
    bzero(&bar, sizeof (struct bar));
    return bar;    
}

struct foo bar = empty_foo();
bar.id=42;
bar.current_route = new_route;
update(&bar);

हालाँकि, यह बहुत संभव नहीं है, यदि 0 संरचना में फ़ील्ड के लिए एक मान्य मान है।


एक नेटवर्क एक अच्छा कारण है =) लेकिन अगर -1 'नॉट केयर केयर' मूल्य है, तो बस खाली_फू () फ़ंक्शन को नियंत्रित करें, और उस दृष्टिकोण का उपयोग करें?
gnud

क्षमा करें, मैंने गलती से डाउनवोट क्लिक किया और केवल बाद में देखा !!! मैं किसी भी तरह से पूर्ववत नहीं कर सकता कि अब जब तक कोई संपादन न हो ...
Ciro Santilli 郝海东 to to to to to
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.