2010-01-18 13 views
6

चलो हमारे पास एक साधारण संरचना (पीओडी) है।सी ++ सदस्य लेआउट

struct xyz 
{ 
    float x, y, z; 
}; 

क्या मुझे लगता है कि निम्नलिखित कोड ठीक है? क्या मुझे लगता है कि कोई अंतराल नहीं है? मानक क्या कहता है? क्या यह पीओडी के लिए सच है? क्या कक्षाओं के लिए यह सच है?

xyz v; 
float* p = &v.x; 
p[0] = 1.0f; 
p[1] = 2.0f; // Is it ok? 
p[2] = 3.0f; // Is it ok? 
+0

मुझे लगता है कि जब तक सभी 'फ्लोट' सदस्य एक-दूसरे के बगल में हों, लेकिन मैंने मानक नहीं पढ़ा है :) – kennytm

+1

@ केनीटीएम जो आपने कहा वह सच है, लेकिन महत्वपूर्ण हिस्सा ".. सभी फ्लोट सदस्य एक दूसरे के बगल में हैं .. "। यह मानने का कोई कारण नहीं है कि वे सभी एक दूसरे के बगल में हैं। –

+1

चूंकि यह तकनीक विवादास्पद हो सकती है, इसलिए बेहतर संरचना 'संरचना' के अंदर एक सरणी है। –

उत्तर

10

यहां जवाब थोड़ा मुश्किल है। सी ++ मानक कहता है कि पीओडी डेटा प्रकारों में सी लेआउट संगतता गारंटी होगी (Reference)। सी कल्पना की धारा 9.2 के अनुसार एक struct के सदस्यों अनुक्रमिक क्रम में बाहर रखी हो जाएगा अगर

  1. कोई पहुँच संशोधक अंतर
  2. डेटा प्रकार

तो साथ नहीं संरेखण मुद्दों है हां यह समाधान तब तक काम करेगा जब तक कि float में वर्तमान प्लेटफ़ॉर्म पर एक संगत संरेखण है (यह प्लेटफ़ॉर्म शब्द का आकार है)। तो यह 32 बिट प्रोसेसर के लिए काम करना चाहिए लेकिन मेरा अनुमान है कि यह 64 बिट के लिए असफल होगा। अनिवार्य रूप से कहीं भी sizeof(void*)sizeof(float)

+1

मुझे लगता है कि यह 64 बिट मशीनों पर भी काम कर सकता है (भले ही आपके पास मानक की गारंटी नहीं है): यदि फ्लोट 64 बिट तक बढ़ता है तो सब ठीक है (आकार (फ्लोट) == प्लेटफार्म शब्द का आकार)। यदि फ्लोट 32 बिट पर रहता है तो संरचना के अंत में 32 बिट की पैडिंग हो सकती है लेकिन फ्लोट्स क्रमिक क्रम में बिना पैडिंग के रख सकते हैं। लेकिन यह सब अनुमान लगा रहा है और अभी भी प्रत्येक 32 बिट फ्लोट के लिए 64 बिट का उपयोग कर कुछ अजीब 64 बिट मशीन हो सकती है :-) – mmmmmmmm

+2

प्रैक्टिस में यह किसी भी प्लेटफॉर्म पर काम करना चाहिए जिसे मैं सोच सकता हूं। जब तक आकार (फ्लोट) == alignment_of (फ्लोट), अनिवार्य रूप से। लेकिन यह मानक द्वारा गारंटी नहीं है, क्योंकि संरेखण कार्यान्वयन-परिभाषित है। मुझे नहीं लगता कि आकार (शून्य *) के साथ इसके साथ कुछ लेना देना है। – jalf

+0

@jalf, संरेखण की मेरी समझ सीमित है और मुझे सूचक आकार के संदर्भ में संरेखण के बारे में सोचना पड़ता है, इसलिए मैं आपकी समझ को यहां रोकता हूं – JaredPar

4

नहीं, पहले क्षेत्र को छोड़कर ऐसा करना ठीक नहीं है।

सी ++ मानकों से:

9,2 कक्षा सदस्यों
एक पॉड-struct वस्तु के लिए एक सूचक, उपयुक्त रूप से एक reinterpret_cast का उपयोग कर परिवर्तित, अपने प्रारंभिक सदस्य को इंगित करता है (या कि सदस्य अगर बिट-फ़ील्ड है, फिर उस इकाई में जिसमें यह रहता है) और इसके विपरीत। [नोट: इसलिए अनाम गद्दी एक पॉड-struct वस्तु के भीतर, उचित संरेखण को प्राप्त हो सकता है लेकिन इसकी शुरुआत में नहीं, आवश्यक के रूप में ।

2

हार्डवेयर पर निर्भर करता है। मानक स्पष्ट रूप से पीओडी कक्षाओं को अनिर्दिष्ट और अप्रत्याशित पैडिंग की अनुमति देता है। मैंने इसे सी ++ विकिपीडिया पेज पर देखा और आपके लिए spec संदर्भ के साथ फुटनोट पकड़ लिया।

^एक बी आईएसओ/आईईसी (2003)। आईएसओ/आईईसी 14882: 2003 (ई): प्रोग्रामिंग भाषाएं - सी ++ §9.2 कक्षा के सदस्य [class.mem] पैरा। 17

व्यावहारिक दृष्टि से, हालांकि, आम हार्डवेयर और compilers पर यह ठीक हो जाएगा।

5

यह मानक द्वारा गारंटी नहीं है, और कई प्रणालियों पर काम नहीं करेगा। कारण हैं:

  • संकलक लक्ष्य प्लेटफ़ॉर्म के लिए उपयुक्त संरचना सदस्यों को संरेखित कर सकता है, जिसका अर्थ 32-बिट संरेखण, 64-बिट संरेखण, या कुछ और हो सकता है।
  • फ्लोट का आकार 32 बिट्स या 64 बिट्स हो सकता है। इस बात की कोई गारंटी नहीं है कि यह संरचना सदस्य संरेखण के समान है।

कि p[1]xyz.y रूप में एक ही स्थान पर हो सकता है, या यह आंशिक रूप से ओवरलैप हो सकता है, या बिल्कुल नहीं इसका मतलब यह है।

+0

जैसा कि जेरेडपायर ने उल्लेख किया है, यह सच नहीं है।यदि आपके पास केवल फ्लैट सदस्य हैं और कोई एक्सेस विनिर्देश नहीं है, तो संरेखण सरणी के समान होगा। – MartinStettner

+1

हालांकि, यदि आप अंतर्निर्मित प्रकार के संरेखण और आकारों को जानते हैं, तो पीओडी संरचना सदस्यों की व्यवस्था बिना किसी संशोधित संशोधक के अनुमानित है। यह महत्वपूर्ण है, उदाहरण के लिए, स्मृति-मैप किए गए हार्डवेयर इंटरफेस का वर्णन करने वाली संरचनाओं की घोषणा करना। – moonshadow

+0

@ मार्टिनस्टेटनर: जेरेडपार का जवाब इस उत्तर का विरोधाभास नहीं करता है। जब वह काम करता है या काम करने की संभावना नहीं है तो वह थोड़ा और अधिक विस्तृत हो जाता है। एक प्रणाली के लिए 4-बाइट फ्लोट पर 8-बाइट संरेखण चाहते हैं, उदाहरण के लिए, और मानक निश्चित रूप से इसे मना नहीं करता है। –

1

मानक की आवश्यकता है कि स्मृति में व्यवस्था के आदेश परिभाषा के आदेश से मेल खाते हैं, लेकिन उन दोनों के बीच मनमाने ढंग से गद्दी अनुमति देता है। यदि आपके पास सदस्यों के बीच पहुंच विनिर्देशक (public:, private: या protected:) है, तो ऑर्डर के बारे में भी गारंटी खो जाती है।

संपादित करें: सभी तीन सदस्यों के विशिष्ट मामले में एक ही आदिम प्रकार (यानी खुद को structs या ऐसा कुछ भी नहीं) के विशिष्ट मामले में आप एक सुंदर निष्पक्ष मौका खड़े हैं - आदिम प्रकारों के लिए, ऑब्जेक्ट का आकार और संरेखण आवश्यकताएं अक्सर होती हैं वही, तो यह काम करता है।

ओटीओएच, यह केवल दुर्घटना से है, और एक ताकत की तुलना में कमजोरी का अधिक होता है; कोड गलत है, इसलिए आदर्श रूप से यह काम पर आने के बजाए तुरंत असफल हो जाएगा, जिस दिन आप कंपनी के मालिक के लिए डेमो दे रहे हैं, जो आपका सबसे महत्वपूर्ण ग्राहक होगा, जिस समय यह होगा (बेशक) सबसे जघन्य संभव फैशन में असफल ...

0

से आपका कोड ठीक है (जब तक यह केवल उसी वातावरण में उत्पन्न डेटा को संभालता है)। संरचना को स्मृति में रखा जाएगा जैसा घोषित किया गया है कि यह पीओडी है। हालांकि, आम तौर पर, एक गोचाचा होता है जिसके बारे में आपको अवगत होना चाहिए: संकलक संरचना में पैडिंग डालने के लिए प्रत्येक सदस्य की संरेखण आवश्यकताओं का पालन किया जाएगा।

था अपने उदाहरण दिया गया

struct xyz 
{ 
    float x; 
    bool y; 
    float z; 
}; 

तो z संरचना और sizeof (xyz) में 8 बाइट्स के लिए शुरू किया है | 12 float के रूप में (आमतौर पर) 4 बाइट संरेखित हैं हो गया होता।

इसी तरह, मामले में

struct xyz 
{ 
    float x; 
    bool y; 
}; 

sizeof (xyz) == 8, सुनिश्चित करने के लिए ((xyz *) PTR) +1 एक सूचक है कि एक्स के संरेखण आवश्यकताओं का अनुसरण करता है देता है।

चूंकि संरेखण आवश्यकताओं/प्रकार के आकार कंपाइलर्स/प्लेटफॉर्म के बीच भिन्न हो सकते हैं, ऐसे कोड सामान्य पोर्टेबल में नहीं हैं।

1

नहीं, आप यह नहीं मान सकते कि कोई अंतराल नहीं है। आप आर्किटेक्चर की जांच कर सकते हैं, और यदि वहां नहीं हैं और आपको पोर्टेबिलिटी की परवाह नहीं है, तो यह ठीक रहेगा।

लेकिन 32-बिट फ्लोट के साथ 64-बिट आर्किटेक्चर की कल्पना करें। संकलक 64-बिट सीमाओं पर struct के तैरता संरेखित कर सकते हैं और अपने

p[1] 

आप जंक दे देंगे, और

p[2] 

आप दे देंगे आप क्या सोचते हैं अपने

p[1] 
से हो रही

& सी।

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

+1

बीटीडब्ल्यू, फ्लोट * किसी भी * मंच पर 64 बिट्स होने की संभावना नहीं है: http://en.wikipedia.org/wiki/IEEE_754-2008 –

+1

ओह, और आपको * गैर-पोर्टेबिलिटी के बारे में चिंता करनी चाहिए। वे एक कुत्ते पर बेकार fleas की तरह हैं: वे अभी तक खुजली नहीं कर सकते हैं, लेकिन अगर आप उन्हें अब साफ नहीं करते हैं, तो वे करेंगे, और जब वे करते हैं तो छुटकारा पाने के लिए और अधिक कठिन होगा। –

+0

दूसरी तरफ, यदि प्लेटफ़ॉर्म में वेक्टर प्रसंस्करण इकाई है, तो यह फ़्लोट को संरेखित कर सकता है जैसे कि चार एक 16-बाइट क्षेत्र में फिट होंगे। (जैसा कि मैंने उपयोग किए गए सभी x86 कंपाइलर्स के मामले में है।) – greyfade

0

जैसा कि अन्य ने इंगित किया है कि संरेखण की गारंटी नहीं है। कई कहते हैं कि यह हार्डवेयर निर्भर है, लेकिन असल में यह संकलक भी निर्भर है। हार्डवेयर कई अलग-अलग प्रारूपों का समर्थन कर सकता है। मुझे याद है कि डेटा को "पैक" करने के लिए पीपीसी कंपाइलर समर्थन pragmas। आप इसे 'मूल' सीमाओं पर पैक कर सकते हैं या इसे 32 बिट सीमाओं आदि पर मजबूर कर सकते हैं।

यह समझना अच्छा होगा कि आप क्या करने की कोशिश कर रहे हैं। यदि आप इनपुट डेटा को 'पार्स' करने का प्रयास कर रहे हैं, तो आप वास्तविक पार्सर से बेहतर हैं। यदि आप धारावाहिक करने जा रहे हैं, तो एक वास्तविक धारावाहिक लिखें। यदि आप ड्राइवर के लिए बिट्स को घुमाने की कोशिश कर रहे हैं, तो डिवाइस स्पेक को आपको लिखने के लिए एक विशिष्ट मेमोरी मैप देना चाहिए। फिर आप अपनी पीओडी संरचना लिख ​​सकते हैं, सही संरेखण pragmas निर्दिष्ट करें (अगर समर्थित है) और आगे बढ़ें।

2

संदेह होने पर, आवेदन सूट करने के लिए डेटा संरचना बदलने के लिए:

struct xyz 
{ 
    float p[3]; 
}; 

पठनीयता के लिए आप चाहते हो सकता है पर विचार करने के:

struct Functor 
{ 
    virtual void operator()(const float& f) = 0; 
}; 

struct xyz 
{ 
    void for_each(Functor& ftor) 
    { 
    ftor(p[0]); 
    ftor(p[1]); 
    ftor(p[2]); 
    return; 
    } 
    private: 
    float p[3]; 
} 
:

struct xyz 
{ 
    enum { x_index = 0, y_index, z_index, MAX_FLOATS}; 
    float p[MAX_FLOATS]; 

    float X(void) const {return p[x_index];} 
    float X(const float& new_x) {p[x_index] = new_x;} 

    float Y(void) const {return p[y_index];} 
    float Y(const float& new_y) {p[y_index] = new_y;} 

    float Z(void) const {return p[z_index];} 
    float Z(const float& new_z) {p[z_index] = new_z;} 
}; 

शायद यहां तक ​​कि कुछ अधिक कैप्सूलीकरण जोड़ने

सामान्य रूप से, यदि डेटा संरचना को दो या दो से अधिक अलग तरीकों से इलाज करने की आवश्यकता है, तो शायद डेटा संरचना को फिर से डिजाइन करने की जरूरत है; या कोड।

class idVec4 { 
public: 
    float   x; 
    float   y; 
    float   z; 
    float   w; 
    ... 
    const float * ToFloatPtr(void) const; 
    float *   ToFloatPtr(void); 
    ... 
} 

ID_INLINE const float *idVec4::ToFloatPtr(void) const { 
    return &x; 
} 

ID_INLINE float *idVec4::ToFloatPtr(void) { 
    return &x; 
} 

यह कई सिस्टम पर काम करता है:

1

कयामत III स्रोत कोड पर एक नज़र देता है।

-1

मुझे लगता है कि आप अपने निर्देशांक सदस्यों (.x, .y और .z) के रूप में एक्सेस करने के लिए एक संरचना चाहते हैं, लेकिन आप अभी भी उन्हें एक्सेस करना चाहते हैं, मान लें, ओपनजीएल तरीका (जैसे कि यह एक सरणी था) ।

आप संरचना के [] ऑपरेटर को लागू करने का प्रयास कर सकते हैं ताकि इसे सरणी के रूप में एक्सेस किया जा सके। कुछ की तरह:

struct xyz 
{ 
    float x, y, z; 
    float& operator[] (unsigned int i) 
    { 
    switch (i) 
    { 
    case 0: 
     return x; 
     break; 
    case 1: 
     return y; 
     break; 
    case 2: 
     return z; 
     break; 
    default: 
     throw std::exception 
     break; 
    } 
    } 
}; 
0
  1. संरचना पैकिंग (MSVC में जैसे #pragma pack) http://msdn.microsoft.com/en-us/library/aa273913%28v=vs.60%29.aspx
  2. चर संरेखण (MSVC में __declspec(align( जैसे) http://msdn.microsoft.com/en-us/library/83ythb65.aspx

दो कारक है कि अपनी मान्यताओं बर्बाद कर सकते हैं। फ्लोट आमतौर पर 4 बाइट चौड़े होते हैं, इसलिए ऐसे बड़े चरों को गलत तरीके से गलत करना दुर्लभ होता है। लेकिन यह अभी भी आपके कोड को तोड़ना आसान है।

यह समस्या सबसे अधिक दिखाई देती है जब शॉर्ट्स (जैसे बीएमपी या टीजीए) के साथ बाइनरी रीडिंग हेडर स्ट्रक्चर - pack 1 भूलना आपदा का कारण बनता है।

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