2015-12-11 7 views
7

मान लीजिए कि हम बाइट्स के अनुक्रम के रूप में कुछ डेटा प्राप्त करते हैं, और उस अनुक्रम को संरचना के रूप में दोबारा परिभाषित करना चाहते हैं (कुछ गारंटी है कि डेटा वास्तव में सही प्रारूप में है)। उदाहरण के लिए:यूबी पैदा किए बिना पीओडी संरचना के रूप में बाइट्स के अनुक्रम को फिर से परिभाषित करने के लिए कैसे?

#include <fstream> 
#include <vector> 
#include <cstdint> 
#include <cstdlib> 
#include <iostream> 

struct Data 
{ 
    std::int32_t someDword[629835]; 
    std::uint16_t someWord[9845]; 
    std::int8_t someSignedByte; 
}; 

Data* magic_reinterpret(void* raw) 
{ 
    return reinterpret_cast<Data*>(raw); // BAD! Breaks strict aliasing rules! 
} 

std::vector<char> getDataBytes() 
{ 
    std::ifstream file("file.bin",std::ios_base::binary); 
    if(!file) std::abort(); 
    std::vector<char> rawData(sizeof(Data)); 
    file.read(rawData.data(),sizeof(Data)); 
    if(!file) std::abort(); 
    return rawData; 
} 

int main() 
{ 
    auto rawData=getDataBytes(); 
    Data* data=magic_reinterpret(rawData.data()); 
    std::cout << "someWord[346]=" << data->someWord[346] << "\n"; 
    data->someDword[390875]=23235; 
    std::cout << "someDword=" << data->someDword << "\n"; 
} 

अब यहाँ magic_reinterpret, वास्तव में बुरा है, क्योंकि यह सख्त अलियासिंग नियम टूट जाता है और इस तरह यूबी कारण बनता है।

यूबी का कारण नहीं बनने के लिए इसे कैसे लागू किया जाना चाहिए और memcpy जैसे डेटा की कोई प्रतियां नहीं करनी चाहिए?


संपादित: ऊपर वास्तव में कुछ अपरिवर्तनीय समारोह माना जाता था getDataBytes() कार्य करते हैं। वास्तविक दुनिया का उदाहरण ptrace(2) है, जो लिनक्स पर, request==PTRACE_GETREGSET और addr==NT_PRSTATUS लिखते हैं, x86-64 पर) विभिन्न आकारों के दो संभावित संरचनाओं में से एक लिखते हैं, ट्रेससी सीथर के आधार पर, और आकार देता है। यहां ptrace कॉलिंग कोड भविष्यवाणी नहीं कर सकता है कि यह वास्तव में कॉल करने तक किस प्रकार की संरचना प्राप्त करेगा। फिर यह सही पॉइंटर प्रकार के रूप में प्राप्त होने वाले परिणामों को सुरक्षित रूप से दोबारा कैसे परिभाषित कर सकता है?

+1

अनिवार्य रूप से, संरचना के लिए डेटा के साथ बाइट सरणी कास्टिंग करने के बजाय, एक स्ट्रक्चर उदाहरण लें, इसे बाइट सरणी में डालें (एड्रेस) को डालें और इस सरणी को डेटा के साथ भरें। [जे Pileborg इसे नीचे कोड के रूप में पोस्ट किया गया] – deviantfan

+0

* लेकिन *, जबकि आपने इसे स्वयं का जिक्र किया था, हमेशा int आकार, नकारात्मक संख्या प्रारूप, संरचना संरेखण, पैडिंग, ... – deviantfan

+0

के बारे में सोचने के लिए एक अनुस्मारक ... विशेष रूप से, डेटा होने जा रहा है अधिकांश मशीनों पर 64 बिट्स, लेकिन वहां केवल 56 बिट जानकारी है। –

उत्तर

4

फ़ाइल को बाइट्स की धारा के रूप में नहीं पढ़कर, लेकिन Data संरचनाओं की धारा के रूप में नहीं।

बस उदा।

Data data; 
file.read(reinterpret_cast<char*>(&data), sizeof(data)); 
+0

कृपया प्रश्न का संपादन देखें। – Ruslan

1

मुझे लगता है कि इन सभी char प्रकार (हस्ताक्षर किए, अहस्ताक्षरित, और सादे) के लिए सख्त नियम अलियासिंग के लिए एक विशेष अपवाद नहीं है।

Data* magic_reinterpret(char *raw) 

काम नहीं करता मुझे डर लग रहा: तो मैं तुम सब करने की है लगता है, magic_reinterpret के हस्ताक्षर बदल रहा है। जैसा कि deviantfan द्वारा टिप्पणी की गई है, आप Data को [unsigned] char की श्रृंखला के रूप में पढ़ सकते हैं (या लिख ​​सकते हैं), लेकिन Data के रूप में आप char को पढ़ या लिख ​​नहीं सकते हैं। जोआचिम का जवाब सही है।

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

+0

जबकि 'char' वास्तव में विशेष है, यह आपके जैसा नहीं है। यह द्विपक्षीय नहीं है। बीटीडब्लू।, 'हस्ताक्षरित char' ठीक नहीं है, भले ही सादा' char' हस्ताक्षरित चर के रूप में कार्य करता हो। – deviantfan

+0

अरे! मैंने 4 मिनट पहले उत्तर दिया, इसे ठीक करने के लिए वापस आएं, और किसी को पहले से ही टिप्पणी की है। –

+0

क्या आप इसे और अधिक पसंद करेंगे अगर आप चुप डाउनवॉट्स इकट्ठा करते हैं, यह जानने के बिना कि क्या गलत है? ... एसओ में बहुत से सक्रिय उपयोगकर्ता हैं, सी ++ जैसे टैग में प्रतिक्रियाएं प्राप्त करने में लंबा समय नहीं लगता है। – deviantfan

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

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