2013-02-06 40 views
10

अगर मैं इस तरह एक वस्तु है:क्या यह यूबी 'गद्देदार' बाइट्स तक पहुंचने के लिए है?

struct { 
    uint32_t n; 
    uint8_t c; 
} blob {}; 

फिर वहाँ 3 हो जाएगा बाइट्स 'गद्देदार'।

क्या यह यूबी गद्देदार बाइट तक पहुंचने के लिए है? उदाहरण के लिए:

uint8_t * data = reinterpret_cast<uint8_t*>(&blob); 
std::cout << data[4] << data[5] << data[6] << data[7]; 

मैं पहली बार मान लिया है कि यह शायद यूबी होगा, लेकिन यह है कि अगर सच है तो एक memcpy रूप में अच्छी तरह यूबी होगा:

memcpy(buf, &blob, sizeof(blob)); 

मेरे विशिष्ट प्रश्न हैं:

  • है यह यूबी गद्देदार बाइट्स तक पहुंचने के लिए है?
  • यदि नहीं, तो क्या इसका मतलब यह है कि मान भी परिभाषित किए गए हैं?
+1

चूंकि यह एक बहुत ही नियम-लेविरी प्रश्न है, मुझे यकीन नहीं है कि अंतिम जवाब क्या होगा। मुझे संदेह है कि इसे परिभाषित किया जाएगा (जब तक uint8_t * हस्ताक्षरित char * है), लेकिन शुरुआती मानों को अनिर्दिष्ट छोड़ दिया जाएगा, जैसे कि किसी भी अन्य सदस्य चर को अनियंत्रित छोड़ दिया गया है। अब, यदि आप विनिर्देश उद्धरण चाहते हैं, तो मैं उसे देखने के लिए अधिक समय के साथ किसी को छोड़ देता हूं :) –

+0

मुझे लगता है कि 'uint32_t' को 'sizeof (uint32_t)' के साथ गठबंधन किया गया है। – StackedCrooked

+3

@ पीटर जी: 'आकार (ब्लॉब) 'मानक से कम से कम 5 होना आवश्यक है। चूंकि' uint8_t' मौजूद है, इसलिए यह' CHAR_BIT == 8' और इसलिए 'sizeof (uint32_t) == 4' है। यह तब संरेखण का मामला है कि 'आकार (ब्लॉब) '5 या 8 है। इसे 6 या 7 –

उत्तर

6

नहीं, यह नहीं यूबी गद्दी तक पहुँचने के लिए है, जब संपूर्ण वस्तु शून्य initialised कर दिया गया है या value- (मानक §8.5/5 कि गद्दी 0-बिट initialised है जब वस्तुओं शून्य initialised कर रहे हैं में कहते हैं) प्रारंभिक और यह एक उपयोगकर्ता परिभाषित कन्स्ट्रक्टर के साथ एक वर्ग नहीं है।

+0

तो यदि ऑब्जेक्ट शून्य-प्रारंभिक नहीं था, लेकिन सदस्यों को असाइन किया गया था तो पैड किए गए बाइट्स तक पहुंच * * यूबी होगी? (और तो memcpy होगा?) – StackedCrooked

+0

@Stacked अगर इसे प्रारंभ नहीं किया गया है लेकिन सदस्यों को सौंपा गया है, तो आप सदस्यों की प्रतिलिपि बना सकते हैं लेकिन पूरी वस्तु नहीं। –

+1

@StackedCrooked: यदि इसे प्रारंभ नहीं किया गया है, तो वही बात जो स्मृति के किसी भी अन्य प्रारंभिक टुकड़े के लिए होती है, यहां होगी: यदि आपका प्रोग्राम अनियमित स्मृति के मान पर निर्भर करता है तो आपके पास यूबी है, यदि उस स्मृति का मान अप्रासंगिक है आपका प्रोग्राम, आपके पास यूबी नहीं है (यानी 'memcpy' पूरी तरह ठीक है जब तक कि आपका प्रोग्राम पैडिंग में फ़ील्ड के मान पर निर्भर न हो)। अनियंत्रित अपरिभाषित व्यवहार का अर्थ नहीं है, 'int main() {int a; में कोई अपरिभाषित व्यवहार नहीं है; int बी = ए; वापसी 0; } ' –

0

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

बेशक, एक ऐसी प्रणाली में, आप दर्द की एक पूरी ढेर बस एक बूट के दौरान कुछ बिंदु पर "सभी स्मृति को भरने" करके, से बचने चाहते हैं, क्योंकि यह illagel क्या करना होगा:

struct Blob 
{ 
    uint32_t n; 
    uint8_t c; 
}; 

Blob *b = malloc(sizeof(Blob)*10); 

for(int i = 0; i < 10; i++) 
{ 
    b[i].n = i; 
    b[i].c = i; 
} 


... 

Blob a[3]; 

memcpy(a, &b[1], sizeof(a)); // Copies 3 * Blob objects, including padding. 

अब, चूंकि बी [x] के सभी बिट्स सेट नहीं किए गए हैं, इसलिए यह समानता/ईसीसी त्रुटियों के कारण, memcpy में डेटा की प्रतिलिपि बनाने में विफल हो सकता है। वह बहुत बुरा होगा। लेकिन साथ ही, कंपाइलर को सभी पैडिंग क्षेत्रों को "सेट" करने के लिए मजबूर नहीं किया जा सकता है।

मेरा निष्कर्ष यह है कि यह यूबी है, लेकिन जब तक विशेष परिस्थितियां न हों तब तक समस्याएं उत्पन्न होने की संभावना नहीं है। निश्चित रूप से, आप उपरोक्त प्रकार के memcpy कोड को बहुत सारे कोड में देखेंगे।

+0

किसी भी अनियंत्रित स्मृति को पढ़ना यूबी है, न केवल अजीब सिस्टम पर। –

+0

हां, और यदि आप आवंटित स्मृति को भरने वाले 'मॉलोक' का उपयोग करते हैं, तो यह उन संरचनाओं की प्रतिलिपि बनाने के लिए 'memcpy' का उपयोग करके खराब होगा, जिन्हें आप प्रत्येक बाइट नहीं लिख सकते हैं। फिर भी, हम इस तरह के कोड को नियमित रूप से देखते हैं, क्या आप सहमत नहीं हैं? यह काम करता है, क्योंकि लगभग हमेशा, स्मृति ओएस द्वारा भरा जाता है, या तो बूट के दौरान, या बाद में उदाहरण के लिए आवेदन के लिए स्मृति प्रदान करने के ओएस चरण। लेकिन मुझे यकीन है कि यह यूबी उस मामले को कवर करने के लिए है जहां स्मृति शुरू नहीं हुई है। –

0

सी में यह अनिर्धारित व्यवहार नहीं है। केवल समय आप (इस तरह की वस्तुओं में गद्दी के रूप में) अप्रारंभीकृत सामान तक पहुँचने से अपरिभाषित व्यवहार मिलता है, तब होता है जब वस्तु स्वत: भंडारण अवधि है और कभी नहीं इसका पता ले लिया है:

6.3.2.1.2: तो lvalue स्वचालित भंडारण अवधि का एक ऑब्जेक्ट निर्दिष्ट करता है जो हो सकता है जो रजिस्टर स्टोरेज क्लास (कभी उसका पता नहीं लिया गया था) के साथ घोषित किया गया था, और उस ऑब्जेक्ट को प्रारंभ नहीं किया गया है (प्रारंभकर्ता के साथ घोषित नहीं किया गया है और इसके लिए कोई असाइनमेंट पहले किया गया है उपयोग करने के लिए), व्यवहार अपरिभाषित है।

लेकिन इस मामले में, आप पता (& के साथ) ले जा रहे हैं, तो व्यवहार में अच्छी तरह से परिभाषित किया गया है (कोई त्रुटि हो जाएगा), लेकिन आप किसी भी यादृच्छिक मूल्य मिल सकता है।

सी ++ में सभी दांव बंद होते हैं, जैसा आमतौर पर मामला होता है।

+0

जैसा स्टीव जेसॉप ने उल्लेख किया है, 4.1/1 कहता है कि यदि किसी प्रोग्राम को एक अनियमित वस्तु के एक लवल-टू-रावल्यू रूपांतरण की आवश्यकता होती है, तो यह यूबी है। –

+0

आह, सी ++ टैग से चूक गया - सी में यह अच्छी तरह परिभाषित है। सी ++ spec यहां टूटा हुआ है। –

0

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

0

एक पीओडी संरचना कम से कम आकार (संरचना) बाइट्स (जिसमें कोई भी पैडिंग बाइट शामिल है) की स्मृति के एक संगत ब्लॉक में रहेंगे। पैडिंग बाइट्स (यदि वे मौजूद हैं) तक पहुंच केवल यूबी होगी यदि इसे पहली बार प्रारंभ नहीं किया गया था।

memset(&s, 0, sizeof(s)); 

यह पैडिंग समेत सभी बाइट्स शुरू करेगा। जिसके बाद पैडिंग से पढ़ना यूबी नहीं होगा।

बेशक, memset() एक सी-आईएसएम है और हम इसे कभी भी C++ में नहीं करेंगे, है ना?