2009-12-29 22 views
36

मैंने सुना है एक कह रही है कि C++ प्रोग्रामर memset से बचना चाहिए,क्या सी ++ प्रोग्रामर यादृच्छ से बचने चाहिए?

class ArrInit { 
    //! int a[1024] = { 0 }; 
    int a[1024]; 
public: 
    ArrInit() { memset(a, 0, 1024 * sizeof(int)); } 
}; 

तो कोड ऊपर पर विचार, अगर आप memset का उपयोग नहीं करते, आप कैसे एक [1..1024] शून्य? क्या गलत से भर कर सकता है सी ++ में मेमसेट के साथ?

धन्यवाद।

+3

क्या आप कारण बता सकते हैं कि आपको सी ++ में मेमसेट क्यों नहीं करना चाहिए? मुझे नहीं पता कि मेमसेट क्यों करना सी ++ में किसी भी समस्या का कारण बनना चाहिए। अगर मैं गलत हूं कृपया मुझे सही। धन्यवाद! – Jay

+0

उन्होंने शायद इसे "शून्य-आउट क्लास ऑब्जेक्ट्स में मेमसेट का उपयोग न करें" के संदर्भ में सुना है। –

+2

@Jay: वे ऊपर ठीक है। लेकिन वर्ग ऑब्जेक्ट को शून्य करने के लिए मेमसेट का उपयोग करना (केवल एक सदस्य नहीं) एक अच्छा विचार नहीं है। यह विशेष रूप से problomatic है अगर ऑब्जेक्ट में ऐसे सदस्य होते हैं जिनमें रचनाकार होते हैं (जो कुछ प्रारंभिक कार्य करते हैं)। –

उत्तर

44

मुद्दा इतना memset() में निर्मित प्रकार, यह उन्हें वर्ग पर उपयोग कर रहा है (उर्फ गैर पॉड) प्रकार पर उपयोग नहीं कर रहा है। ऐसा करने से लगभग हमेशा गलत चीज होती है और अक्सर घातक चीज होती है - उदाहरण के लिए, वर्चुअल फ़ंक्शन टेबल पॉइंटर पर टंपल हो सकता है।

+1

क्या आप एक उदाहरण जोड़ सकते हैं जहां मेमसेट का उपयोग गलत है? –

+6

किसी वर्चुअल फ़ंक्शन वाले किसी भी वर्ग पर मेमसेट का उपयोग करना खराब होने की संभावना है। –

+0

@ ओटो: क्योंकि आकार (वर्ग) एक डेटा सदस्य के रूप में वर्चुअल फ़ंक्शन टेबल पॉइंटर का इलाज करेगा। – Jichao

23

शून्य आरंभ इस तरह दिखना चाहिए:

class ArrInit { 
    int a[1024]; 
public: 
    ArrInit(): a() { } 
}; 

memset का उपयोग कर के रूप में, वहाँ तरीके के उपयोग और अधिक मजबूत बनाने के लिए (जैसे सभी कार्यों के साथ के रूप में) के एक जोड़े हैं: हार्ड-कोड से बचने सरणी के आकार और प्रकार:

memset(a, 0, sizeof(a)); 

अतिरिक्त संकलन समय के लिए जाँच करता है यह भी सुनिश्चित करें कि a वास्तव में एक सरणी है बनाने के लिए संभव है (ताकि sizeof(a) मतलब होगा):

template <class T, size_t N> 
size_t array_bytes(const T (&)[N]) //accepts only real arrays 
{ 
    return sizeof(T) * N; 
} 

ArrInit() { memset(a, 0, array_bytes(a)); } 

लेकिन गैर चरित्र प्रकार के लिए, मैं सिर्फ मूल्य आप इसका उपयोग के साथ भरने के लिए कल्पना 0 है, और शून्य प्रारंभ पहले से ही एक तरह से या किसी अन्य रूप में उपलब्ध होना चाहिए।

+0

यदि सरणी को गैर-शून्य के साथ प्रारंभ करना चाहते हैं तो क्या होगा? – Jichao

+0

आप ब्रेसिज़ के अंदर इच्छित कोई भी मूल्य डाल सकते हैं (उदा। ArrInit(): a() {5}) और यह उस मान के साथ सरणी को प्रारंभ करेगा। – Pace

+1

आपको एहसास है कि मुझे बस इतना करना है कि वर्चुअल फ़ंक्शन वाले कुछ वर्ग में आपके उदाहरण में 'int' बदलें, और आपका कोड vptr को मिटा देगा, है ना? आप समझ रहे हैं कि आपदाओं को थोड़ा सुरक्षित तरीके से कैसे पहुंचाया जाए। –

-3

सी ++ में आपको नया उपयोग करना चाहिए। आपके उदाहरण में सरल सरणी के मामले में इसका उपयोग करने में कोई वास्तविक समस्या नहीं है। हालांकि, अगर आपके पास कक्षाओं की एक सरणी थी और इसे प्रारंभ करने के लिए मेमसेट का इस्तेमाल किया गया था, तो आप कक्षाओं को सही ढंग से तैयार नहीं कर पाएंगे।

इस पर विचार करें:

class A { 
    int i; 

    A() : i(5) {} 
} 

int main() { 
    A a[10]; 
    memset (a, 0, 10 * sizeof (A)); 
} 

उन तत्वों में से प्रत्येक के लिए निर्माता नहीं बुलाया जाएगा, इसलिए सदस्य चर मैं 5. करने के लिए सेट नहीं किया जाएगा आप नए बजाय प्रयोग किया:

A a = new A[10]; 

सरणी में प्रत्येक तत्व की तुलना में इसके कन्स्ट्रक्टर को कॉल किया जाएगा और मुझे 5 पर सेट किया जाएगा।

+0

मुझे शून्य पर प्रारंभ करने के बारे में सवाल याद आया, और मेमसेट और नए के बीच अंतर पर केंद्रित था। – Casey

+1

@ कैसी: मेरे जी ++ कंपाइलर में 'ए [1]' कन्स्ट्रक्टर को कॉल करता है, और मेम्बेर वैरिएबल मुझे 5 पर सेट किया जाएगा। – Jichao

+3

'ए ए [10] = नया ए [10];' वैध सी ++ नहीं है । आप दूसरी भाषा के साथ सी ++ को भ्रमित कर रहे हैं। –

0

आपका कोड ठीक है। मैंने सोचा कि सी ++ में एकमात्र समय जहां मेमसेट खतरनाक है, जब आप कुछ के साथ कुछ करते हैं:
YourClass instance; memset(&instance, 0, sizeof(YourClass);

मेरा मानना ​​है कि यह आपके उदाहरण में आंतरिक डेटा को शून्य कर सकता है कि संकलक बनाया गया है।

8

यह "बुरा" है क्योंकि आप अपना इरादा लागू नहीं कर रहे हैं।

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

इसके अलावा, यह अधिक कुशल नहीं है।

; Line 12 
    xor eax, eax 
    mov ecx, 1024    ; 00000400H 
    mov edi, edx 
    rep stosd 

काफी वास्तव में क्या memset संभावना वैसे भी करने के लिए संकलन होता है कौन सा -

class ArrInit 
{ 
public: 
    ArrInit(); 
private: 
    int a[1024]; 
}; 

ArrInit::ArrInit() 
{ 
    for(int i = 0; i < 1024; ++i) { 
     a[i] = 0; 
    } 
} 


int main() 
{ 
    ArrInit a; 
} 

दृश्य C++ 2008 अनुकूलन के साथ 32 बिट चालू साथ इस संकलन करने के लिए लूप संकलित करता है। लेकिन अगर आप मेमसेट का उपयोग करते हैं तो कंपाइलर के लिए और अधिक अनुकूलन करने के लिए कोई गुंजाइश नहीं है, जबकि आपके इरादे को लिखकर यह संभव है कि संकलक आगे अनुकूलन कर सके, उदाहरण के लिए यह ध्यान दें कि प्रत्येक तत्व बाद में इसे इस्तेमाल करने से पहले किसी अन्य चीज़ पर सेट किया जाता है ताकि प्रारंभिकरण को अनुकूलित किया जा सकता है, जो संभवतः लगभग आसानी से नहीं कर सका अगर आपने मेमसेट का उपयोग किया था।

+0

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

+0

'जो मेमसेट किसी भी तरह से संकलित हो सकता है, उतना ही काफी सटीक है।' नहीं, मेमसेट एक साधारण 'रेप स्टॉस्ड' – zhangyoufu

49

सी ++ std::fill या std::fill_n में बेहतर विकल्प हो सकता है, क्योंकि यह सामान्य है और इसलिए वस्तुओं और साथ ही पीओडी पर भी काम कर सकता है। हालांकि, memset बाइट्स के कच्चे अनुक्रम पर काम करता है, और इसलिए गैर-पीओडी शुरू करने के लिए कभी भी इसका उपयोग नहीं किया जाना चाहिए। भले ही std::fill का अनुकूलित कार्यान्वयन आंतरिक रूप से memset पर कॉल करने के लिए विशेषज्ञता का उपयोग कर सकता है यदि प्रकार एक पीओडी है।

+1

से अधिक जटिल और कुशल हो सकता है मैं std :: के बारे में भूल गया तो मुझे इस से +1 +1। हां, एक सी ++ फ़ंक्शन विशेष रूप से कंटेनरों को भरने के लिए डिज़ाइन किया गया है, इसलिए इसका उपयोग करें! – jcoder

+4

पीओडी का अर्थ क्या है? – Jichao

+6

http://en.wikipedia.org/wiki/Plain_old_data_structures – Reunanen

9

क्या C++ memset साथ कुछ गड़बड़ है ज्यादातर एक ही बात है कि सी memset में memset साथ कुछ गड़बड़ है है, शारीरिक शून्य-बिट पैटर्न के साथ स्मृति क्षेत्र भरता है, जबकि मामलों की लगभग 100% में वास्तव में आप तार्किक साथ एक सरणी भरने की जरूरत है इसी प्रकार के शून्य-मान। सी भाषा में, memset केवल पूर्णांक प्रकारों के विपरीत मेमोरी को ठीक से शुरू करने की गारंटी है (और के लिए इसकी वैधता सभी पूर्णांक प्रकारों के विपरीत, पूर्ण प्रकार के विपरीत, सी भाषा विनिर्देश में अपेक्षाकृत हाल ही की गारंटी है)। किसी भी फ़्लोटिंग पॉइंट मानों को ठीक से शून्य पर सेट करने की गारंटी नहीं है, यह उचित नल-पॉइंटर्स का उत्पादन करने की गारंटी नहीं है।

बेशक, उपर्युक्त अत्यधिक पेडेंटिक के रूप में देखा जा सकता है, क्योंकि दिए गए प्लेटफ़ॉर्म पर सक्रिय अतिरिक्त मानकों और सम्मेलनों में memset की प्रयोज्यता बढ़ सकती है, लेकिन मैं अभी भी ओकम के रेज़र सिद्धांत का पालन करने का सुझाव दूंगा यहां: किसी भी अन्य मानकों और सम्मेलनों पर भरोसा न करें जबतक कि आपको वास्तव में वास्तव में नहीं करना है। सी ++ भाषा (साथ ही सी) कई भाषा-स्तर की विशेषताएं प्रदान करती है जो आपको अपने समग्र ऑब्जेक्ट को उचित प्रकार के उचित शून्य मानों के साथ सुरक्षित रूप से प्रारंभ करने देती हैं। अन्य उत्तरों ने पहले से ही इन विशेषताओं का उल्लेख किया है।

+1

भौतिक और तार्किक शून्य के बीच क्या अंतर है? – Adil

+0

@Adil भौतिक शून्य स्मृति में स्पष्ट वास्तविक "ऑल-शून्य" बिट पैटर्न है। तार्किक शून्य [संभावित रूप से गैर-शून्य] बिट पैटर्न है जिसे भाषा द्वारा किसी प्रकार के शून्य मान के रूप में व्याख्या किया जाता है (हमारे मामले में सी या सी ++)। – AnT

0

कक्षाओं पर लागू होने पर बुरेपन के अलावा, memset भी त्रुटि प्रवण है। तर्कों को बाहर करने के लिए, या sizeof भाग को भूलना बहुत आसान है। कोड आमतौर पर इन त्रुटियों के साथ संकलित होगा, और चुपचाप गलत बात करते हैं। बग का लक्षण बहुत बाद में प्रकट नहीं हो सकता है, जिससे इसे ट्रैक करना मुश्किल हो जाता है।

memset पॉइंटर्स और फ़्लोटिंग पॉइंट जैसे कई सादे प्रकारों के साथ भी समस्याग्रस्त है। कुछ प्रोग्रामर सभी बाइट्स को 0 पर सेट करते हैं, मानते हैं कि पॉइंटर्स न्यूल होंगे और फ्लोट 0.0 होगा। यह एक पोर्टेबल धारणा नहीं है।

+0

बाइनरी शून्य पर पॉइंटर्स और फ़्लोटिंग-पॉइंट नंबर सेट करना आमतौर पर काम करता है, लेकिन मैं आदत में नहीं जाना चाहता हूं। फिर भी, आईईईई फ्लोटिंग-पॉइंट मानक अधिक से अधिक जुड़ा हुआ हो जाता है, और यह सभी बिट्स-शून्य को 0.0 के रूप में व्याख्या करता है। –

+0

@ डेविड: हाँ, यह आमतौर पर काम करता है, लेकिन किसी दिन आप एक मंच पर होंगे जहां यह नहीं है। –

0

इसका उपयोग करने का कोई वास्तविक कारण नहीं है, कुछ मामलों के अलावा लोगों ने यह भी बताया कि कोई भी इसका उपयोग नहीं करेगा, लेकिन इसका उपयोग करने के लिए कोई वास्तविक लाभ नहीं है जब तक कि आप मेमगार्ड या कुछ भर नहीं लेते।

0

संक्षिप्त उत्तर 1024.

std::vector<int> a(1024); // Uses the types default constructor, "T()". 

की एक प्रारंभिक आकार के साथ एक std :: वेक्टर का उपयोग करने के सभी तत्वों का प्रारंभिक मूल्य "एक" 0 होगा std :: वेक्टर के रूप में, हो सकता है (आकार) कन्स्ट्रक्टर (साथ ही वेक्टर :: आकार बदलना) सभी तत्वों के लिए डिफ़ॉल्ट कन्स्ट्रक्टर का मान कॉपी करता है।बिल्ट-इन प्रकार के (उर्फ आंतरिक प्रकार, या फली) के लिए, आप प्रारंभिक मूल्य की गारंटी 0 होने के लिए कर रहे हैं:

int x = int(); // x == 0 

इस प्रकार का है कि "एक" कम से कम उपद्रव के साथ बदलने के लिए उपयोग करता है की अनुमति होगी, यहां तक ​​कि उस के लिए एक वर्ग के

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

1

यह एक पुरानी धागा है, लेकिन यहाँ एक दिलचस्प मोड़ है:

class myclass 
{ 
    virtual void somefunc(); 
}; 

myclass onemyclass; 

memset(&onemyclass,0,sizeof(myclass)); 

काम करता है पूरी तरह से अच्छी तरह से!

हालांकि,

myclass *myptr; 

myptr=&onemyclass; 

memset(myptr,0,sizeof(myclass)); 

वास्तव में शून्य करने के लिए virtuals सेट (यानी somefunc() ऊपर)।

यह देखते हुए कि बड़ी संख्या में प्रत्येक सदस्य को 0 से सेट करने की तुलना में स्मृति बहुत तेज है, मैं उम्र के लिए उपरोक्त पहली याददाश्त कर रहा हूं और कभी भी कोई समस्या नहीं थी।

तो वास्तव में दिलचस्प सवाल यह है कि यह कैसे काम करता है? मुझे लगता है कि संकलक वास्तव में शून्य के वर्चुअल टेबल से पहले सेट करना शुरू कर देता है ... कोई विचार?

+0

"यह क्रैश नहीं होता है या कुछ भी गलत रूप से गलत करता है जिसे मैं देख सकता हूं" और "यह काम करता है" बहुत कुछ नहीं है। AFAICT उपरोक्त दोनों कोड स्निपेट समान हैं, लेकिन एक बार जब आप अपरिभाषित व्यवहार का आह्वान करना शुरू कर देते हैं, तो सभी दांव बंद हो जाते हैं। सबसे अधिक संभावना है कि उपर्युक्त में से कोई भी प्रोग्राम केवल विशिष्ट परिस्थितियों में काम करेगा (दिखाई देगा), और अन्य परिस्थितियों में बुरी तरह टूट जाएगा (उदाहरण के लिए एक अलग कंपाइलर, या ओएस, या सीपीयू आर्किटेक्चर पर) –

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