2009-10-27 19 views
29

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

उत्तर

8

प्रत्येक वर्ग घोषणा के क्रम में अपने डेटा सदस्यों को बताता है।
कंपाइलर को एक्सेस कुशल बनाने के लिए सदस्यों के बीच पैडिंग करने की अनुमति है (लेकिन इसे फिर से ऑर्डर करने की अनुमति नहीं है)।

कैसे dynamic_cast<> कार्य एक कंपाइलर कार्यान्वयन विवरण है और मानक द्वारा परिभाषित नहीं किया गया है। यह सभी संकलक द्वारा उपयोग किए गए एबीआई पर निर्भर करेगा।

reinterpret_cast<> ऑब्जेक्ट के प्रकार को बदलकर काम करता है। एकमात्र चीज जिसे आप उस काम की गारंटी दे सकते हैं वह यह है कि एक पॉइंटर को शून्य * पर कास्टिंग करना और उसे वापस करने के लिए पॉइंटर क्लास आपको एक ही पॉइंटर देगा।

+3

आपका पहला बिंदु पूरी तरह से सही नहीं है। आपके पास केवल एक ही गारंटी है कि एक ही एक्सेस ब्लॉक में सदस्यों के पास एक निर्धारित आदेश होगा। यदि आप इसे अपने चरम पर ले जाना चाहते थे, तो आप कह सकते हैं कि यहां तक ​​कि पहुंच समान है जहां ऑर्डर की गारंटी नहीं है। –

+0

@ रिचर्ड। मुझे यकीन नहीं है कि मैं आपको समझता हूं। कंपाइलर को तत्वों को फिर से ऑर्डर करने की अनुमति नहीं है (यह सी के साथ बैक वार्ड संगतता के लिए है)। एक एक्सेस ब्लॉक क्या है। क्या आप मुझे मानक के सही हिस्से पर इंगित कर सकते हैं कि आपको अपनी जानकारी मिल रही है? –

+2

एक एक्सेस ब्लॉक (वास्तव में, एक्सेस विनिर्देशक) 'सार्वजनिक:', 'निजी:', और 'संरक्षित:' कक्षा में है। मुझे यह आलेख मिला: http://www.embedded.com/design/218600150?pgno=1 बेहद उपयोगी है, और यही वह जगह है जहां मैंने पहली बार सीखा कि रिचर्ड ने अपनी टिप्पणी में क्या उल्लेख किया था। मैंने सी ++ मानक को वहां जोड़ा, और पीजी पर देखा। 1 9 85 (सेकेंड 9.2 क्लॉज 12) यह कहता है: "विभिन्न एक्सेस नियंत्रण वाले गैर-स्थैतिक डेटा सदस्यों के आवंटन का आदेश अनिर्दिष्ट है" –

4

उत्तर है, "यह जटिल है"। गतिशील कास्ट बस ऑफसेट के साथ पॉइंटर्स समायोजित नहीं करता है; यह वास्तव में अपने काम करने के लिए ऑब्जेक्ट के अंदर आंतरिक पॉइंटर्स पुनर्प्राप्त कर सकता है। जीसीसी इटेनियम के लिए डिजाइन किए गए एबीआई का पालन करता है लेकिन अधिक व्यापक रूप से लागू किया जाता है। आप यहां गोरी विवरण पा सकते हैं: Itanium C++ ABI

4

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

  1. रनटाइम प्रकार की जानकारी
  2. गैर आभासी आधार वस्तुओं और अपने डेटा (शायद घोषणा के क्रम में)।
  3. सदस्य चर
  4. वर्चुअल बेस ऑब्जेक्ट्स और उनका डेटा (शायद कुछ डीएफएस पेड़ खोज आदेश में)।

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

जब यह आता है, reinterpret_cast पॉइंटर के सी ++ डेटा प्रकार को बदलता है और कुछ भी नहीं करता है, तो आप बेहतर जानते थे कि आप जानते हैं कि आप इसका उपयोग करते समय क्या कर रहे हैं, अन्यथा आप गड़बड़ी के लिए उत्तरदायी हैं चीजें बुरी तरह से। dynamic_cast static_cast (पॉइंटर को बदलने में) के समान ही बहुत कुछ करता है सिवाय इसके कि यह रनटाइम प्रकार की जानकारी का उपयोग करता है, यह पता लगाने के लिए कि क्या यह दिए गए प्रकार पर जा सकता है, और ऐसा कैसे करें। फिर, वह सब कंपाइलर विशिष्ट है। ध्यान दें कि आप dynamic_castvoid* नहीं कर सकते हैं क्योंकि इसे पता होना चाहिए कि रनटाइम प्रकार की जानकारी कहां मिलनी है ताकि यह अपने सभी अद्भुत रनटाइम चेक कर सके।

1

इस सवाल पहले से ही http://dieharddeveloper.blogspot.in/2013/07/c-memory-layout-and-process-image.html में उत्तर दिया जाता है यहाँ वहाँ से एक अंश है: प्रक्रिया का पता स्थान के बीच में, वहाँ एक क्षेत्र साझा वस्तुओं के लिए आरक्षित किया गया है। जब कोई नई प्रक्रिया बनाई जाती है, तो प्रक्रिया प्रबंधक पहले निष्पादन योग्य से दो खंडों को स्मृति में मानचित्रित करता है। यह तब प्रोग्राम के ईएलएफ हेडर को डीकोड करता है। यदि प्रोग्राम हेडर इंगित करता है कि निष्पादन योग्य किसी साझा लाइब्रेरी के विरुद्ध जुड़ा हुआ था, तो प्रक्रिया प्रबंधक (पीएम) प्रोग्राम शीर्षलेख से गतिशील दुभाषिया का नाम निकाल देगा। गतिशील दुभाषिया एक साझा लाइब्रेरी को इंगित करता है जिसमें रनटाइम लिंकर कोड होता है।

+1

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

9

मेमोरी लेआउट ज्यादातर कार्यान्वयन के लिए छोड़ दिया जाता है। मुख्य अपवाद यह है कि किसी दिए गए एक्सेस विनिर्देशक के लिए सदस्य चर उनकी घोषणा के क्रम में होंगे।

§ एक ही पहुँच नियंत्रण (खण्ड 11) के साथ एक (गैर संघ) वर्ग के 9.2.14

Nonstatic डेटा सदस्यों आवंटित किए जाते हैं ताकि बाद में सदस्यों को एक वर्ग वस्तु के भीतर उच्च पते हैं। गैर-स्थैतिक के विभिन्न अभिगम नियंत्रण वाले डेटा सदस्यों को आवंटित करने का आदेश निर्दिष्ट नहीं है (11)। कार्यान्वयन संरेखण आवश्यकताओं के कारण दो निकट सदस्य एक दूसरे के तुरंत बाद आवंटित नहीं किए जा सकते हैं; तो वर्चुअल फ़ंक्शंस (10.3) और वर्चुअल बेस क्लासेस (10.1) के प्रबंधन के लिए स्थान की आवश्यकता हो सकती है।

सदस्य चर के अलावा, एक वर्ग या struct सदस्य चर, आधार वर्ग, आभासी समारोह प्रबंधन (जैसे एक आभासी तालिका), और गद्दी और इन आंकड़ों के संरेखण के subobjects लिए जगह उपलब्ध कराने की जरूरत है। यह कार्यान्वयन पर निर्भर है लेकिन Itanium एबीआई विनिर्देश एक लोकप्रिय पसंद है। जीसीसी और क्लैंग इसका पालन करते हैं (कम से कम एक डिग्री तक)।

http://mentorembedded.github.io/cxx-abi/abi.html#layout

इटेनियम ABI निश्चित रूप से सी का हिस्सा नहीं मानक है ++ और बाध्यकारी नहीं है। अधिक विस्तृत जानकारी प्राप्त करने के लिए आपको अपने कार्यान्वयन के दस्तावेज और औजारों को चालू करने की आवश्यकता है। क्लैंग कक्षाओं के मेमोरी लेआउट को देखने के लिए एक उपकरण प्रदान करता है। उदाहरण के लिए, निम्नलिखित:

class VBase { 
    virtual void corge(); 
    int j; 
}; 

class SBase1 { 
    virtual void grault(); 
    int k; 
}; 

class SBase2 { 
    virtual void grault(); 
    int k; 
}; 

class SBase3 { 
    void grault(); 
    int k; 
}; 

class Class : public SBase1, SBase2, SBase3, virtual VBase { 
public: 
    void bar(); 
    virtual void baz(); 
    // virtual member function templates not allowed, thinking about memory 
    // layout and vtables will tell you why 
    // template<typename T> 
    // virtual void quux(); 
private: 
    int i; 
    char c; 
public: 
    float f; 
private: 
    double d; 
public: 
    short s; 
}; 

class Derived : public Class { 
    virtual void qux(); 
}; 

int main() { 
    return sizeof(Derived); 
} 

वर्ग की स्मृति लेआउट का उपयोग करता है एक स्रोत फ़ाइल बनाने के बाद, बजना स्मृति लेआउट का पता चलता है।

$ clang -cc1 -fdump-record-layouts layout.cpp 

Class के लिए लेआउट:

*** Dumping AST Record Layout 
    0 | class Class 
    0 | class SBase1 (primary base) 
    0 |  (SBase1 vtable pointer) 
    8 |  int k 
    16 | class SBase2 (base) 
    16 |  (SBase2 vtable pointer) 
    24 |  int k 
    28 | class SBase3 (base) 
    28 |  int k 
    32 | int i 
    36 | char c 
    40 | float f 
    48 | double d 
    56 | short s 
    64 | class VBase (virtual base) 
    64 |  (VBase vtable pointer) 
    72 |  int j 
    | [sizeof=80, dsize=76, align=8 
    | nvsize=58, nvalign=8] 

इस बजना सुविधा पर अधिक एली बेन्देर्स्क्य के ब्लॉग पर पाया जा सकता:

http://eli.thegreenplace.net/2012/12/17/dumping-a-c-objects-memory-layout-with-clang/

जीसीसी एक समान उपकरण प्रदान करता है, `-fdump वर्ग-पदानुक्रम '। वर्ग ऊपर दिए गए के लिए, यह (अन्य बातों के अलावा) प्रिंट:

Class Class 
    size=80 align=8 
    base size=58 base align=8 
Class (0x0x141f81280) 0 
    vptridx=0u vptr=((& Class::_ZTV5Class) + 24u) 
    SBase1 (0x0x141f78840) 0 
     primary-for Class (0x0x141f81280) 
    SBase2 (0x0x141f788a0) 16 
     vptr=((& Class::_ZTV5Class) + 56u) 
    SBase3 (0x0x141f78900) 28 
    VBase (0x0x141f78960) 64 virtual 
     vptridx=8u vbaseoffset=-24 vptr=((& Class::_ZTV5Class) + 88u) 

यह सदस्य चर शुमार नहीं करता है (या कम से कम मैं इसे करने के लिए कैसे प्राप्त करने के लिए पता नहीं है) लेकिन आप बता सकते हैं वे क्लैंग लेआउट में बस 28 और 64 ऑफसेट के बीच होना चाहिए।

आप देख सकते हैं कि एक बेस क्लास primary के रूप में बाहर किया गया है। यह सूचक के समायोजन की आवश्यकता को हटा देता है जब Class को SBase1 के रूप में उपयोग किया जाता है।

बराबर जीसीसी के लिए है:

$ g++ -fdump-class-hierarchy -c layout.cpp 

विजुअल C++ के लिए बराबर है:

cl main.cpp /c /d1reportSingleClassLayoutTest_A 

देखें: https://blogs.msdn.microsoft.com/vcblog/2007/05/17/diagnosing-hidden-odr-violations-in-visual-c-and-fixing-lnk2022/

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