2016-08-18 6 views
8

के साथ वस्तु को कॉपी मैं है निम्नलिखित कोड:डबल मुक्त या भ्रष्टाचार त्रुटि memcpy

#include <iostream> 
#include <string> 
#include <cstring> 

struct test { 
    std::string name; 
    size_t id; 
}; 


int main() { 
    test t; 
    t.name = "147.8.179.239"; 
    t.id = 10; 

    char a[sizeof(t)] = ""; 
    std::memcpy(a, &t, sizeof(t)); 

    test b; 
    std::memcpy(&b, a, sizeof(t)); 

    std::cout << b.name << " " << b.id << std::endl; 
} 

जब मैं यह संकलन और इसे चलाने, यह मेरे निम्न त्रुटि देता है:

147.8.179.239 10 
*** Error in `./test': double free or corruption (fasttop): 0x0000000000bf9c20 *** 
Aborted (core dumped) 

यह बदल जाता है कोड बाहर परिणाम मुद्रित कर सकते हैं। लेकिन मैं इस त्रुटि को कैसे ठीक कर सकता हूं?

+0

@ स्टैकप्टर वीएलए क्या है? – Johnnylin

+2

परिवर्तनीय लंबाई सरणी। संकलन समय पर इसका आकार अज्ञात है, और सी ++ में अवैध है। – stackptr

+1

@ जॉनीनीलिन परिवर्तनीय लंबाई सरणी। इसे मानक सी ++ में प्रतिबंधित है (हालांकि इसे कुछ कंपाइलर एक्सटेंशन द्वारा अनुमति दी गई है)। –

उत्तर

17

memcpy का उपयोग करके आप जिस तरह से हैं, आपके पास दो std::string ऑब्जेक्ट्स हैं जो बिल्कुल समान हैं। इसमें किसी भी पॉइंटर्स शामिल हैं जो वे आंतरिक रूप से उपयोग कर सकते हैं। तो जब प्रत्येक ऑब्जेक्ट के लिए विनाशक चलता है, तो वे दोनों एक ही सूचक को मुक्त करने का प्रयास कर रहे हैं।

यही कारण है कि आपको कॉपी कन्स्ट्रक्टर का उपयोग करने की आवश्यकता है या एक को दूसरे को असाइन करना है (यानी ओवरराइड operator= का उपयोग करें)। यह उन कार्यान्वयन अंतरों के बारे में जानता है और उन्हें सही तरीके से संभालता है, यानी यह गंतव्य ऑब्जेक्ट के लिए एक अलग मेमोरी बफर आवंटित करता है।

यदि आप std::string में निहित स्ट्रिंग निकालना चाहते हैं, तो आपको को ज्ञात प्रतिनिधित्व के लिए क्रमबद्ध करने की आवश्यकता है। फिर आप को इसे परिवर्तित करने के लिए deserialize कर सकते हैं।

std::string s1 = "hello"; 
printf("len=%zu, str=%s\n",s1.size(),s1.c_str()); 

// serialize 
char *c = new char[s1.size()+1]; 
strcpy(c, s1.c_str()); 
printf("c=%s\n",c); 

// deserialize 
std::string s2 = c; 
printf("len=%zu, str=%s\n",s2.size(),s2.c_str()); 

आप अन्य कक्षा वस्तुओं के लिए समान कदम करेंगे।

+0

जो मैं प्राप्त करना चाहता हूं वह एक स्ट्रक्चर को परिवर्तित करना है जिसमें 'std :: string' और अन्य पीओडी प्रकार को चार सरणी में शामिल किया गया है। फिर मैं इसे संचारित करने के लिए सॉकेट का उपयोग कर सकता हूं। आपके विवरण के लिये धन्यवाद। – Johnnylin

+1

@Johnnylin some_string.c_str() आपको स्ट्रिंग की अंतर्निहित चार सरणी देगा, फिर आप उसे भेज सकते हैं, प्रतियों की कोई आवश्यकता नहीं है। (भी .size() आपको सरणी में कितने वर्ण देगा) – Borgleader

+3

@ जॉनीनीलिन फिर आपको बाइट स्तर पर उस डेटा के प्रारूप पर निर्णय लेने की आवश्यकता है क्योंकि सॉकेट बाइट-स्तरीय पाइप प्रदान करते हैं। इसके बाद आपको उस डेटा को कनवर्ट करने के लिए कोड लिखना होगा जिसे आप अपने द्वारा चुने गए प्रारूप में भेजना चाहते हैं ताकि आप इसे भेज सकें। –

12

आप memcpy()test जैसे गैर-पीओडी संरचना नहीं कर सकते हैं। आप पूरी तरह से std::string सदस्य को बर्बाद कर रहे हैं।

आप प्रतिलिपि बनाने वाले सी ++ ऑब्जेक्ट्स की प्रतिलिपि बनाने के लिए उपयोग करें।

+0

तो, दो त्रुटियां हैं? एक परिवर्तनीय लंबाई सरणी है और दूसरा memcpy गैर-पीओडी संरचना है? – Johnnylin

+3

* वीएलए अवैध हैं * अधिक सटीक वे कुछ कंपाइलर्स (जीसीसी और संभवतः क्लैंग) का एक गैर-मानक विस्तार हैं, कहा जा रहा है कि मुझे कोई वीएलए नहीं दिख रहा है। – Borgleader

+0

क्या आप इस कोड का बेहतर संस्करण प्रदान कर सकते हैं? – Johnnylin

6

वास्तविक कारण आप एक डबल मुक्त त्रुटि हो रही है तथ्य यह है कि बजाय अपने चर a और b के लिए एक नया स्ट्रिंग वस्तु बनाने की, तो आप सिर्फ संदर्भ कॉपी करने के लिए पिन किया गया है (एक string वस्तु चर लंबाई का उपयोग कर कार्यान्वित किया जाता है char *)।

string नाशक कब से अपने कार्यक्रम समाप्त होता है, और जैसा कि ऊपर आप दो स्ट्रिंग वस्तुओं एक ही पते की ओर इशारा करते है, तो आप एक डबल मुक्त त्रुटि

यह काम करेगा मिल की तरह @JesperJuhl कहा समझाया इस स्मृति पते को मुक्त कर देते , आपको एक प्रतिलिपि का उपयोग करना चाहिए

#include <iostream> 
#include <string> 
#include <cstring> 

struct test 
{ 
    std::string name; 
    size_t id; 
}; 


int main() 
{ 
    test t; 
    test a; 
    test b; 

    t.name = "147.8.179.239"; 
    t.id = 10; 

    a=t; 
    b=t; 

    std::cout << b.name << " " << b.id << std::endl; 
} 
+0

तो मूल रूप से, मैं एक स्ट्रक्चर को याद नहीं कर सकता जिसमें गैर-पीओडी प्रकार ('स्ट्रिंग') शामिल है, जब तक कि मैं 'std :: string' को' char' array में नहीं बदलता? – Johnnylin

+1

सही, ऑब्जेक्ट्स के लिए कॉपी कन्स्ट्रक्टर का उपयोग करना आम तौर पर अच्छा अभ्यास है। असल में, यदि आप सी ++ का उपयोग नहीं कर रहे हैं तो इसका कोई वैध कारण नहीं होना चाहिए - कॉपी कन्स्ट्रक्टर सभी अतिरिक्त मेमोरी आवंटन को सारणीबद्ध करता है, आपको हुड के नीचे होने के बारे में भी पता नहीं हो सकता है –

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