2009-05-20 8 views
8

मान लीजिए मैं इस struct है (जो संयोग से थोड़ा-क्षेत्रों होते हैं, लेकिन आप परवाह नहीं होना चाहिए):सी में एक परिवर्तनीय पूर्णांक के अनुसार 'संरचना' के सदस्यों तक कैसे पहुंचे?

struct Element { 
    unsigned int a1 : 1; 
    unsigned int a2 : 1; 
    ... 
    unsigned int an : 1; 
}; 

और मैं एक सुविधाजनक तरीके से i'th सदस्य का उपयोग करना चाहते हैं। आइए एक पुनर्प्राप्ति समाधान की जांच करें।
मैं इस समारोह के साथ आया था:

int getval(struct Element *ep, int n) 
{ 
    int val; 
    switch(n) { 
     case 1: val = ep->a1; break; 
     case 2: val = ep->a2; break; 
     ... 
     case n: val = ep->an; break; 
    } 
    return val; 
} 

लेकिन मुझे लगता है एक बहुत सरल समाधान नहीं है। सरणी पहुंच शैली की तरह कुछ, शायद।

मैं ऐसा ही कुछ करने की कोशिश की:

#define getval(s,n) s.a##n 

लेकिन प्रत्याशित यह काम नहीं करता।
क्या कोई अच्छा समाधान है?

+1

मुझे डर है कि हमें परवाह करना चाहिए कि क्या बिट-फ़ील्ड हैं या नहीं: आप एक पॉइंटर को थोड़ा फ़ील्ड सदस्य को परिभाषित नहीं कर सकते हैं, और सरणी एक्सेसिंग शैली का मतलब सूचक हैंडलिंग का तात्पर्य है। – mouviciel

उत्तर

12

जब तक आप struct की अंतर्निहित संरचना के विशिष्ट ज्ञान, वहाँ सी में इस तरह के एक विधि को लागू करने के कोई रास्ता नहीं है

  • के सदस्य सहित समस्याओं कि रास्ते में मिल जाएगा के सभी प्रकार के कर रहे हैं विभिन्न आकारों
  • पैकिंग मुद्दों
  • संरेखण मुद्दों
  • ट्रिक्स bitfields तरह समस्याग्रस्त हो जाएगा

आप अपनी संरचना के लिए हाथ से एक विधि को लागू करने से सबसे अच्छे हैं, जिसकी संरचना के आंतरिक सदस्यों की गहरी समझ है।

+0

हालांकि मैं structs को आवंटित आवंटित करने की गारंटी थी। यदि यह सही है, और आप उन बिट्स की संख्या जानते हैं जिन्हें आपको ऑफ़सेट करने की आवश्यकता है, तो यह * लगता है * जैसे कि यह केवल संरचना सूचक को हटाना और विशेष मान पर सीधे कूदना संभव है। – DevinB

+0

@devinb, संगत हां। लेकिन परिवर्तनीय आकार के सदस्य आपको मिलेंगे। यदि विभिन्न आकार के सदस्य हैं, तो किसी दिए गए सदस्य के ऑफ़सेट की गणना करने का कोई तरीका नहीं है जब तक कि आप संरचना के बारे में सारी जानकारी नहीं जानते। इसलिए एक सामान्य उद्देश्य मैक्रो को परिभाषित करने का कोई तरीका नहीं है। – JaredPar

+0

मैंने आपकी प्रतिक्रिया को गलत तरीके से पढ़ा। मैं क्षमाप्रार्थी हूं। मैं सहमत हूँ। यह सी में पूरी तरह कार्यान्वित है, लेकिन इसे हाथ से किया जाना चाहिए, और इसे संरचना के विशिष्ट विस्तृत ज्ञान की आवश्यकता है। – DevinB

2

नहीं, यह आसान करने का कोई आसान तरीका नहीं है। खासकर बिटफील्ड के लिए, पॉइंटर्स के माध्यम से परोक्ष रूप से पहुंचना मुश्किल है (आप बिटफील्ड का पता नहीं ले सकते हैं)।

आप निश्चित रूप से कुछ इस तरह है कि समारोह को आसान बनाने में कर सकते हैं:

int getval(const struct Element *ep, int n) 
{ 
    switch(n) 
    { 
     case 1: return ep->a1; 
     case 2: return ep->a2; 
     /* And so on ... */ 
    } 
    return -1; /* Indicates illegal field index. */ 
} 

और यह स्पष्ट लगता है कि कैसे कार्यान्वयन आगे एक पूर्वप्रक्रमक मैक्रो case लाइन के लिए विस्तारित का उपयोग करके सरल किया जा सकता है, लेकिन यह सिर्फ है sugar

6

यदि आपका struct में हर क्षेत्र एक int है, तो आप मूल रूप से कहने के लिए

int getval(struct Element *ep, int n) 
{ 
    return *(((int*)ep) + n); 
} 

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

एक अधिक सामान्य समाधान क्षेत्र ऑफसेट की एक सरणी बनाए रखने के लिए होगा:

int offsets[3]; 
void initOffsets() 
{ 
    struct Element e; 
    offsets[0] = (int)&e.x - (int)&e; 
    offsets[1] = (int)&e.y - (int)&e; 
    offsets[2] = (int)&e.z - (int)&e; 
} 

int getval(struct Element *ep, int n) 
{ 
    return *((int*)((int)ep+offsets[n])); 
} 

यह समझ है कि आप अपने struct के पूर्णांक क्षेत्रों में से किसी के लिए getval कॉल करने के लिए सक्षम हो जाएगा में काम करेंगे, भले ही आपके पास अपनी संरचना में अन्य गैर-int फ़ील्ड हों, क्योंकि ऑफ़सेट सभी सही होंगे।हालांकि, यदि आपने गैर-int फ़ील्ड में से किसी एक पर getval पर कॉल करने का प्रयास किया है तो यह एक पूरी तरह से गलत मान वापस करेगा।

बेशक, आप प्रत्येक डेटा प्रकार के लिए एक अलग फ़ंक्शन लिख सकते हैं, उदा।

double getDoubleVal(struct Element *ep, int n) 
{ 
    return *((double*)((int)ep+offsets[n])); 
} 

और फिर आप जो भी डेटाटाइप चाहते हैं उसके लिए उचित फ़ंक्शन को कॉल करें। संयोग से, अगर आप सी ++ उपयोग कर रहे थे आप कह सकते हैं

template<typename T> 
T getval(struct Element *ep, int n) 
{ 
    return *((T*)((int)ep+offsets[n])); 
} 

की तरह कुछ और फिर इसे जो भी डेटाप्रकार आप चाहते हैं, उसके लिए काम करेगा।

+0

यह सुंदर है। –

+0

यह काम नहीं करता है। ओपी आकार 1 बिट का थोड़ा सा क्षेत्र इस्तेमाल किया। आप 4 आकार बाइट में पॉइंटर लौट रहे होंगे। यह कोड अन-गठबंधन पॉइंटर्स का उत्पादन करेगा और जब उचित संरेखण पर संदर्भित किया गया है तो संरचना के बनाम 1 बिट को 4 बाइट्स (अधिकांश प्लेटफॉर्म पर) पढ़ा जाएगा। – JaredPar

+2

@ जेरेडपार: इस उत्तर की पहली पंक्ति "यदि आपकी संरचना में हर क्षेत्र एक int है"। जो यह नहीं है, लेकिन फिर प्रश्नकर्ता ने यह भी कहा कि "आपको परवाह नहीं करना चाहिए कि खेतों का प्रकार क्या है", और हम परवाह करते हैं। बहुत। क्योंकि बिटफील्ड अजीब हैं। –

0

यदि संरचना वास्तव में वर्णन के रूप में सरल है, तो आप एक सरणी (या एक सरणी के लिए एक कलाकार) और कुछ बिट-एक्सेस जादू (How do you set, clear and toggle a single bit in C? में) के साथ एक संघ का उपयोग कर सकते हैं।

जैसा कि जेरेड कहते हैं, सामान्य मामला हार्ड है।

+0

हां - जब तक आप विश्वास करते हैं कि अंतराल और पैडिंग इत्यादि के मामले में डेटा कैसे संग्रहीत किया जाता है, तो –

0

क्यों संरचना में getval() का निर्माण नहीं?

struct Whang { 
    int a1; 
    int a2; 
    int getIth(int i) { 
     int rval; 
     switch (i) { 
      case 1: rval = a1; break; 
      case 2: rval = a2; break; 
      default : rval = -1; break; 
     } 
     return rval; 
    } 
};  

int _tmain(int argc, _TCHAR* argv[]) 
{ 
     Whang w; 
    w.a1 = 1; 
    w.a2 = 200; 

    int r = w.getIth(1); 

    r = w.getIth(2); 

    return 0; 
} 

getIth()Whang के आंतरिक भागों की जानकारी होती है, और जो यह निहित के साथ सौदा कर सकते हैं।

+1

"संरचना में गेटवल() क्यों नहीं बनाते?" क्योंकि यह सी है, सी ++ नहीं? –

+0

एएके, यह सादा सी का उपयोग करने के बाद से एक लंबा समय रहा है ... गलत दिशा के लिए खेद है। – Number8

6

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

यदि आप चर सूचकांक द्वारा बिट्स तक पहुंचना चाहते हैं, तो संभवतया आपके बिटकफील्ड को फ्लैग बिट्स वाले पूर्णांक के साथ प्रतिस्थापित करना सबसे अच्छा है। वेरिएबल द्वारा एक्सेस वास्तव में नहीं है कि बिटफील्ड क्या हैं: ए 1 ... मूल रूप से स्वतंत्र सदस्य हैं, बिट्स की सरणी नहीं।

आप कुछ इस तरह कर सकता है:

struct Element { 
    unsigned int a1 : 1; 
    unsigned int a2 : 1; 
    ... 
    unsigned int an : 1; 
}; 

typedef unsigned int (*get_fn)(const struct Element*); 

#define DEFINE_GETTER(ARG) \ 
    unsigned int getter_##ARG (const struct Element *ep) { \ 
     return ep-> a##ARG ; \ 
    } 

DEFINE_GETTER(1); 
DEFINE_GETTER(2); 
... 
DEFINE_GETTER(N); 

get_fn jump_table[n] = { getter_1, getter_2, ... getter_n}; 

int getval(struct Element *ep, int n) { 
    return jump_table[n-1](ep); 
} 

और पुनरावृत्ति के कुछ चाल जहां एक ही हेडर कई बार शामिल द्वारा बचा जा सकता है, हर बार अलग ढंग से एक मैक्रो परिभाषित किया। हेडर प्रत्येक मैक के लिए एक बार मैक्रो फैलाता है ... एन

लेकिन मुझे विश्वास नहीं है कि यह इसके लायक है।

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

+0

वाह। क्या निर्माण है। यह निश्चित रूप से व्यावहारिक प्रतीत नहीं होता है लेकिन मैं आपके द्वारा प्रस्तुत किए गए कुछ विचारों को ले जाऊंगा। धन्यवाद। –

+0

> यदि मुझे याद है कि सी 0 गारंटी देता है कि > एक ही प्रकार के सभी संरचनाओं के सदस्यों के पास > एक सरणी यह समाचार है। किसी और ने यह सुना है? कल्पना कीजिए कि आप एक संरचना को परिभाषित करते हैं जो 5 बाइट लंबा है। एक 32 बिट मशीन पर जो आपको आमतौर पर 3 बाइट अंतराल के साथ छोड़ देगी। मुझे आश्चर्य है कि एक सरणी उन अंतराल को भी दिखाएगी? –

+0

संरचना के सदस्यों पर निर्भर करता है। 5 वर्णों वाली 5-बाइट संरचना 5 के आकार के साथ आ सकती है और किसी सरणी या संरचना में कोई अंतराल नहीं आ सकता है। एक इंट 32 और एक चार युक्त 5-बाइट स्ट्रक्चर 8 के आकार और दोनों मामलों में अंतराल के साथ बाहर आ जाएगा। –

0

मुझे लगता है कि अपने वास्तविक समाधान अपने struct में bitfields का उपयोग नहीं करने के लिए है, लेकिन इसके बजाय या तो एक सेट प्रकार या थोड़ा सरणी परिभाषित करते हैं।

+0

मुझे बिट्स की एक विशिष्ट संख्या की आवश्यकता है और समस्या यह है कि सी किसी भी प्रकार के निश्चित बिट्स की गारंटी नहीं देता है। उदाहरण के लिए, char को बिल्कुल 8 बिट्स की गारंटी नहीं है, और इसी तरह ... –

+0

@Leif: फिर C99 से शामिल करें, और 32 झंडे को स्टोर करने के लिए uint32_t का उपयोग करें। यदि आप सी 8 9 पर फंस गए हैं, तो एक जोर दें कि CHAR_BITS> = 8 और वर्णों का उपयोग करें, या फिर 6 बिट प्रति चार स्टोर करें यदि आप अनजाने में सतर्क रहना चाहते हैं: सी के मूल वर्णमाला में 64 से अधिक वर्ण हैं, इसलिए चार 6 बिट से कम नहीं हो सकता है। मुझे लगता है कि पॉज़िक्स CHAR_BIT = 8 को वैसे भी गारंटी दे सकता है, याद नहीं है। –

+0

@onebyone मुझे डर है कि मैं सी 99 पुस्तकालयों का उपयोग नहीं कर सकता। हम सी 99 के साथ काम नहीं करते हैं, और मैं गैर-मानक पुस्तकालयों का भी उपयोग नहीं कर सकता। जोरदार विचार के लिए, मैं इसे देख लूंगा। मुझे नहीं पता कि यह अभी तक क्या है। इसके अलावा, वर्णों में बिट्स की संख्या के लिए कोई ऊपरी सीमा नहीं है, इसलिए 8 की निचली सीमा बहुत अधिक नहीं करती है। –

0

मैं कोड पीढ़ी का सुझाव देता हूं।अपने संरचनाओं क्षेत्रों की बड़ी राशि शामिल नहीं है, तो आप स्वत: प्रत्येक क्षेत्र के लिए या क्षेत्रों की एक श्रृंखला के लिए दिनचर्या पैदा करते हैं और उन्हें इस्तेमाल कर सकते हैं:

val = getfield_aN(myobject, n); 

या

val = getfield_foo(myobject); 
0

आप चाहते हैं

int getval(struct Element *ep, int n) 

और नाम से:

दोनों तत्व सूचकांक का उपयोग कर अपने संरचना का उपयोग

तो आप स्विच को बनाए रखने के लिए कुछ कठिन परिश्रम कर रहे हैं जैसे हर किसी ने सुझाव दिया है।

यदि, हालांकि, आप जो करना चाहते हैं वह इंडेक्स द्वारा एक्सेस और कभी भी नाम से नहीं है, तो आप थोड़ा अधिक रचनात्मक हो सकते हैं।

सबसे पहले, एक क्षेत्र के प्रकार को परिभाषित:

typedef struct _FieldType 
{ 
    int size_in_bits; 
} FieldType; 

और फिर एक संरचना परिभाषा बनाने के लिए:

FieldType structure_def [] = { {1}, {1}, {1}, {4}, {1}, {0} }; 

ऊपर आकार 1, 1, 1, 4 के पांच तत्वों के साथ एक संरचना को परिभाषित करता है और 1 बिट्स। अंतिम {0} परिभाषा के अंत को चिह्नित करता है। एक तत्व

Element *CreateElement (FieldType *field_defs) 
{ 
    /* calculate number of bits defined by field_defs */ 
    int size = ?; 
    /* allocate memory */ 
    Element *element = malloc (sizeof (Element) + (size + 7)/8); /* replace 7 and 8 with bits per char */ 
    element->fields = field_defs; 
    return element; 
} 

और फिर उपयोग करने के लिए:

typedef struct _Element 
{ 
    FieldType *fields; 
} Element; 

एक Element का एक उदाहरण बनाने के लिए:

अब एक तत्व प्रकार बनाने

int GetValue (Element *element, int field) 
{ 
    /* get number of bits in fields 0..(field - 1) */ 
    int bit_offset = ?; 
    /* get char offset */ 
    int byte_offset = sizeof (Element) + bit_offset/8; 
    /* get pointer to byte containing start of data */ 
    char *ptr = ((char *) element) + byte_offset; 
    /* extract bits of interest */ 
    int value = ?; 
    return value; 
} 

स्थापना मान समान है मूल्य प्राप्त करने के लिए, केवल अंतिम भाग को बदलने की जरूरत है।

आप FieldType संरचना को विस्तारित मूल्य के प्रकार के बारे में जानकारी शामिल करने के लिए उपरोक्त को बढ़ा सकते हैं: char, int, float, आदि, और उसके बाद प्रत्येक प्रकार के एक्सेसर्स लिखते हैं जो निर्धारित प्रकार के विरुद्ध आवश्यक प्रकार की जांच करता है।

0

आप अपने struct

  • कम से कम 32 (या 64) में पहली

    1. केवल bitfields, या सभी bitfields है, तो bitfields

    तो इस समाधान आपके लिए है।

    #include <stdio.h> 
    #include <stdint.h> 
    
    struct Element { 
        unsigned int a1 : 1; 
        unsigned int a2 : 1; 
        unsigned int a3 : 1; 
        unsigned int a4 : 1; 
    }; 
    
    #define ELEMENT_COUNT 4 /* the number of bit fields in the struct */ 
    
    /* returns the bit at position N, or -1 on error (n out of bounds) */ 
    int getval(struct Element* ep, int n) 
    { 
        if(n > ELEMENT_COUNT || n < 1) 
        return -1; 
    
        /* this union makes it possible to access bit fields at the beginning of 
        the struct Element as if they were a number. 
        */ 
        union { 
        struct Element el; 
        uint32_t bits; 
        } comb; 
    
        comb.el = *ep; 
        /* check if nth bit is set */ 
        if(comb.bits & (1<<(n-1))) { 
        return 1; 
        } else { 
        return 0; 
        } 
    } 
    
    int main(int argc, char** argv) 
    { 
        int i; 
        struct Element el; 
    
        el.a1 = 0; 
        el.a2 = 1; 
        el.a3 = 1; 
        el.a4 = 0; 
    
        for(i = 1; i <= ELEMENT_COUNT; ++i) { 
        printf("el.a%d = %d\n", i, getval(&el, i)); 
        } 
    
        printf("el.a%d = %d\n", 8, getval(&el, 8)); 
    
        return 0; 
    } 
    
  • +0

    क्या यह समाधान पोर्टेबल है? यह एक इकाई के अंदर बिट्स के संरेखण पर निर्भर करता है, है ना? –

    +0

    हालांकि संघ का विचार शांत है। –

    0

    एली-courtwright समाधान पर लेकिन क्षेत्र ऑफसेट की सरणी का उपयोग किए बिना आधार पर ...... यदि आप एक संरचना इस तरह सूचक क्षेत्र युक्त है, हो सकता है आप लिख सकते हैं:

    struct int_pointers 
    { 
        int *ptr1; 
        int *ptr2; 
        long *ptr3; 
        double *ptr4; 
        std::string * strDescrPtr; 
    
    }; 
    

    तो फिर तुम जानते हैं कि हर सूचक एक 4 बाइट संरचना करने के लिए एक सूचक से ऑफसेट है, तो आप लिख सकते हैं:

    struct int_pointers ptrs; 
    int i1 = 154; 
    int i2 = -97; 
    long i3 = 100000; 
    double i4 = (double)i1/i2; 
    std::string strDescr = "sample-string"; 
    ptrs.ptr1 = &i1; 
    ptrs.ptr2 = &i2; 
    ptrs.ptr3 = &i3; 
    ptrs.ptr4 = &i4; 
    ptrs.strDescrPtr = &strDescr; 
    

    तो, उदाहरण के लिए, एक पूर्णांक मूल्य के लिए आप लिख सकते हैं:

    int GetIntVal (struct int_pointers *ep, int intByteOffset) 
    { 
        int * intValuePtr = (int *)(*(int*)((int)ep + intByteOffset)); 
        return *intValuePtr; 
    } 
    

    द्वारा यह कॉलिंग:

    012,
    int intResult = GetIntVal(&ptrs,0) //to retrieve the first int value in ptrs structure variable 
    
    int intResult = GetIntVal(&ptrs,4) //to retrieve the second int value in ptrs structure variable 
    

    और अन्य लोगों के लिए संरचना फ़ील्ड मान (अन्य विशिष्ट कार्यों को लिखना और सही बाइट ऑफसेट मान (4 का एकाधिक) का उपयोग करना)।

    0

    हालांकि ओपी निर्दिष्ट करता है कि हमें संरचना की सामग्री की परवाह नहीं करनी चाहिए, क्योंकि वे केवल बिटफील्ड हैं, एक एन बनाने के लिए एक char या int (या जो भी डेटा प्रकार आकार आवश्यक है) का उपयोग करना संभव होगा इस मामले में -bit "सरणी"?

    void writebit(char *array, int n) 
    { 
        char mask = (1 << n); 
        *array = *array & mask; 
    } 
    

    चार प्रकार के साथ बदलकर चार प्रकार के साथ प्रतिस्थापित किया गया है यदि लंबे समय तक "सरणी" की आवश्यकता होती है। सुनिश्चित नहीं है कि यह अन्य structs में एक निश्चित समाधान है, लेकिन इसे एक समान रीडबिट funcition के साथ यहां काम करना चाहिए।

    संबंधित मुद्दे