2010-01-11 9 views
9

मुझे हाल ही में एक ऐसे मामले का सामना करना पड़ा जहां मुझे परीक्षण परिणामों के सत्यापन के लिए दो फाइलों (सुनहरा और अपेक्षित) की तुलना करने की आवश्यकता है और भले ही दोनों फाइलों के लिए लिखा गया डेटा समान था, फाइलें मेल नहीं खातीं।सी/सी ++ में सरणी शुरू करने के लिए यह अच्छा अभ्यास है?

आगे की जांच पर, मैंने पाया कि एक संरचना है जिसमें कुछ पूर्णांक और 64 बाइट्स की चार सरणी शामिल है, और अधिकांश मामलों में चार सरणी के सभी बाइट्स का उपयोग नहीं किया जा रहा था और सरणी से अप्रयुक्त फ़ील्ड यादृच्छिक डेटा और यह मेल नहीं खा रहा था।

इससे मुझे सवाल पूछा गया कि क्या सी/सी ++ में सरणी को प्रारंभ करना अच्छा अभ्यास है, जैसा कि यह जावा में किया जाता है?

उत्तर

23

स्मृति का उपयोग करने से पहले स्मृति/चर शुरू करना अच्छा अभ्यास है - अनियंत्रित चर उन बगों का एक बड़ा स्रोत हैं जो अक्सर ट्रैक करने में बहुत मुश्किल होते हैं।

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

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

+7

केवल कुछ संकेत जो एक ही दिशा में जाते हैं: अपने चर के लिए सबसे छोटे दायरे का उपयोग करें। घोषणा के बाद सीधे शुरू करें। केवल एक उद्देश्य के लिए चर का उपयोग करें। यदि आप अपना चर बदलना नहीं चाहते हैं तो कॉन्स का उपयोग करें मूल्य। इन संकेतों का मुख्य कारण: वे पठनीयता में सुधार करते हैं और आर कोड परिवर्तन के दौरान सूक्ष्म बग के अवसर को प्रेरित करें। कोड इन संकेतों का पालन किए बिना अब बगफ्री हो सकता है - लेकिन उनके बाद प्रत्येक कोड परिवर्तन के साथ इसे बग मुक्त रखना आसान है। –

+1

जब भी संभव हो, एक कथन में परिभाषित करें और आरंभ करें। –

+1

नहीं, अपने चर को प्रारंभ करें जहां यह उचित है। लूप वैरिएबल के लिए लूप वैरिएबल प्रारंभ होने पर लूप के लिए अधिक पठनीय है। लूप से पहले आपका चर घोषित कर दिया गया हो सकता है। शॉर्टलैंड में –

1

यदि आप एक C++ सरणी में मानों को प्रारंभ नहीं करते हैं, तो मान कुछ भी हो सकते हैं, इसलिए यदि आप अनुमानित परिणाम चाहते हैं तो उन्हें शून्य करने के लिए अच्छा अभ्यास होगा।

लेकिन यदि आप एक शून्य समाप्त टर्मिंग स्ट्रिंग की तरह चार सरणी का उपयोग करते हैं, तो आप इसे उचित फ़ंक्शन के साथ फ़ाइल में लिखने में सक्षम होना चाहिए।

हालांकि सी ++ में अधिक ओओपी समाधान का उपयोग करना बेहतर हो सकता है। अर्थात। वैक्टर, तार, इत्यादि

+0

नहीं, आप अप्रारंभीकृत स्मृति का उपयोग करता है, तो आप अपने प्रोग्राम में बग है। व्यवस्थित आरंभ चर अक्सर अनावश्यक लेखन पहुंच उत्पन्न करते हैं। –

5

एक सरणी में एक अनिर्धारित मान का उपयोग अपरिभाषित व्यवहार में परिणाम। इस प्रकार कार्यक्रम अलग-अलग परिणामों का उत्पादन करने के लिए स्वतंत्र है। इसका मतलब यह हो सकता है आपकी फ़ाइलों को थोड़ा अलग अंत, या प्रोग्राम क्रैश हो जाता है, या प्रोग्राम आपके हार्ड ड्राइव प्रारूप, या प्रोग्राम उन नाक बाहर उड़ान भरने के लिए राक्षसों का कारण बनता है कि (http://catb.org/jargon/html/N/nasal-demons.html)

इसका मतलब यह नहीं कि आप की जरूरत है जब आप सरणी बनाते हैं तो अपने सरणी मानों को परिभाषित करें, लेकिन आपको यह सुनिश्चित करना होगा कि आप इसका उपयोग करने से पहले किसी भी सरणी मान को प्रारंभ करें। निश्चित रूप से यह सुनिश्चित करने का सबसे आसान तरीका यह है कि जब आप सरणी बनाते हैं तो ऐसा करना है।

MyStruct array[10]; 
printf("%f", array[2].v); // POTENTIAL BANG! 
array[3].v = 7.0; 
... 
printf("%f", array[3].v); // THIS IS OK. 

वहाँ शून्य

MyPODStruct bigArray[1000] = { 0 }; 
+0

+1, मैं इस शैली का उपयोग एक उचित राशि का उपयोग करता हूं। इसके बिना किसी सबूत के सावधानी बरतने का एक शब्द, लेकिन मेरा मानना ​​है कि = {0} प्रारूप को Win32 ZeroMemory (http://msdn.microsoft.com/en-us/library/aa366920%28VS.85) से अधिक अनुकूलित किया जा सकता है % 29.aspx) एपीआई, कंपाइलर्स इच्छाओं और अनुकूलन सेटिंग्स के अधीन है। – dirtybird

+0

इसे केवल अनुकूलित किया जा सकता है यदि संकलक साबित कर सकता है कि यह एक अवलोकन अंतर नहीं करेगा (यानी, सभी मान वैसे भी सेट किए गए हैं)। इस मामले में, एक फ़ाइल में सरणी लिखी जा रही है, यह निश्चित रूप से एक अवलोकन अंतर बना देगा। – caf

0

सबसे पहले सभी सदस्यों आरंभ करने के लिए एक अच्छा आशुलिपि है मत भूलना कि फली की विशाल सरणी के लिए, आप सरणियों, चर, आदि को प्रारंभ करना चाहिए यदि ऐसा है तो नहीं कर रही होगी अपने कार्यक्रम की शुद्धता के साथ गड़बड़।

दूसरा, ऐसा प्रतीत होता है कि इस विशेष मामले में, सरणी को प्रारंभ नहीं करने से मूल कार्यक्रम की शुद्धता प्रभावित नहीं हुई है।इसके बजाए, फाइलों की तुलना करने के लिए प्रोग्राम को फ़ाइल प्रारूप के बारे में पर्याप्त जानकारी नहीं है, यह बताए जाने के लिए कि एक सार्थक तरीके से (पहले प्रोग्राम द्वारा परिभाषित "सार्थक") में भिन्न है या नहीं।

मूल कार्यक्रम के बारे में शिकायत करने के बजाय, मैं तुलनात्मक कार्यक्रम को प्रश्न में फ़ाइल प्रारूप के बारे में अधिक जानने के लिए ठीक कर दूंगा। यदि फ़ाइल प्रारूप अच्छी तरह से प्रलेखित नहीं है तो आपके पास शिकायत करने का एक अच्छा कारण है।

3

मैं दृढ़ विचारों से असहमत हूं कि ऐसा करने से "बग के एक आम स्रोत को समाप्त करना" या "ऐसा नहीं करना आपके कार्यक्रम की शुद्धता के साथ गड़बड़ होगा"। यदि प्रोग्राम प्रारंभिक मानों के साथ काम करता है तो इसमें एक बग है और यह गलत है। मानों को आरंभ करना इस बग को खत्म नहीं करता है, क्योंकि वे अक्सर पहले उपयोग में अपेक्षित मान नहीं रखते हैं। हालांकि, जब वे यादृच्छिक कचरा होते हैं, तो कार्यक्रम हर प्रयास में एक यादृच्छिक तरीके से दुर्घटनाग्रस्त होने की अधिक संभावना है। हमेशा एक ही मूल्य होने से क्रैश होने में अधिक निर्धारक व्यवहार हो सकता है और डीबगिंग को आसान बना दिया जा सकता है।

आपके विशिष्ट प्रश्न के लिए, यह एक फ़ाइल में लिखे जाने से पहले अप्रयुक्त हिस्सों को ओवरराइट करने के लिए भी अच्छी सुरक्षा प्रथा है, क्योंकि उनमें पिछले उपयोग से कुछ शामिल हो सकता है जिसे आप लिखना नहीं चाहते हैं, जैसे पासवर्ड।

+0

+1। मैंने लोगों को देखा है कि एक चर शुरू करने से सभी समस्याओं को हल किया जाता है। नहीं ... यह केवल तब मदद करता है जब आप उस पॉइंटर को न्यूल में प्रारंभ करते हैं कि केवल क्यूम * हमेशा * समस्या को यहां और वहां फसल को देखने और इसके बालों को खींचने के प्रयास को खींचने के बजाय होता है। ऐसे परिदृश्य भी हैं जहां आप डिबगिंग कर रहे हैं और एक ही ब्लॉब को कई बार निष्पादित करते हैं, और यदि आपके पास तर्क दोष है, तो आप अप्रत्याशित पथ नीचे आ सकते हैं क्योंकि परिवर्तनीय/सरणी/आदि बस उसी स्मृति में हवाओं के साथ होता है पिछले मान का उपयोग कर स्थान। – dirtybird

0

मैं कहूंगा कि सी ++ में अच्छा अभ्यास एक सरणी के बजाय std :: vector <> का उपयोग कर रहा है। यह निश्चित रूप से सी के लिए मान्य नहीं है।

1

ध्यान रखें कि अनियंत्रित सरणी रखने से प्रदर्शन जैसे फायदे हो सकते हैं।

यह केवल पढ़ा गया है अनियंत्रित सरणी से। अनियमित स्थानों से कभी भी पढ़े बिना उन्हें चारों ओर रखना ठीक है।

इसके अलावा यदि आपके प्रोग्राम में बग है जो इसे सरणी में अनियमित स्थान से पढ़ता है, तो ज्ञात मान से सभी सरणी को रक्षात्मक रूप से प्रारंभ करके "इसे कवर करना" बग का समाधान नहीं है, और केवल बाद में इसे सतह बना सकता है।

+0

+1 यह एक व्यक्ति के विचार से अधिक होता है। –

1

कोई भी दो शैलियों के बीच अंतर पर एक बड़ा लेख लिख सकता है, जो लोग उन्हें घोषित करते समय चर शुरू करते हैं और जो लोग आवश्यक होने पर उन्हें प्रारंभ करते हैं। मैं किसी ऐसे व्यक्ति के साथ एक बड़ी परियोजना साझा करता हूं जो पहली श्रेणी में है और अब मैं निश्चित रूप से दूसरे प्रकार के अधिक हूं। हमेशा वैरिएबल प्रारंभ करने से अधिक सूक्ष्म बग और समस्याएं सामने आई हैं और मैं यह समझाने की कोशिश करूंगा कि मुझे मिलने वाले मामलों को याद क्यों किया जाए। पहले उदाहरण:

struct NODE Pop(STACK * Stack) 
{ 
    struct NODE node = EMPTY_STACK; 

    if(Stack && Stack->stackPointer) 
    node = Stack->node[--Stack->stackPointer]; 

    return node; 
} 

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

struct NODE Pop(STACK * Stack) 
{ 
    if(Stack && Stack->stackPointer) 
    return Stack->node[--Stack->stackPointer]; 
    return EMPTY_STACK; 
} 

केवल एक प्रतिलिपि करने के लिए इसे नए सिरे से लिखना (और स्पार्क पर पूरक लाभ जहां यह चलाता है, समारोह memcpy को बचा कॉल करने के लिए एक पत्ता समारोह धन्यवाद है और एक नया रजिस्टर खिड़की का निर्माण करने की जरूरत नहीं है)। तो समारोह 4 गुना तेजी से था।

एक और समस्या मुझे औंस मिली लेकिन याद नहीं है कि वास्तव में (इसलिए कोई कोड उदाहरण नहीं है, क्षमा करें)। एक वेरिएबल जिसे घोषित किया गया था, लेकिन इसे एक लूप में इस्तेमाल किया गया था, जिसमें switch एक सीमित राज्य automaton में था। समस्या प्रारंभिक मान automaton के राज्यों में से एक नहीं था और कुछ चरम दुर्लभ मामलों में automaton सही ढंग से काम नहीं करता था। प्रारंभकर्ता को हटाकर, उत्सर्जित संकलक ने चेतावनी दी कि यह स्पष्ट रूप से प्रारंभ होने से पहले चर का उपयोग किया जा सकता है। तब automaton फिक्सिंग आसान था। नैतिकता: एक परिवर्तनीय रक्षात्मक रूप से प्रारंभ करने से संकलक की एक बहुत ही उपयोगी चेतावनी दबाने लग सकती है।

निष्कर्ष: बुद्धिमानी से अपने चर शुरू करें। इसे व्यवस्थित करना एक कार्गो-पंथ का पालन करने के अलावा कुछ भी नहीं है (काम पर मेरा दोस्त खराब मालवाहक-कल्पित व्यक्ति कल्पना कर सकता है, वह कभी भी गेटो का उपयोग नहीं करता है, हमेशा एक चर शुरू करता है, बहुत सारी स्थिर घोषणाओं का उपयोग करता है (यह तेज़ है आप जानते हैं (यह तेज़ है तथ्य यह है यहां तक ​​कि वास्तव स्पार्क 64 बिट पर धीमी गति से) में, सभी कार्यों inline बनाता है, भले ही वे 500 लाइनों (__attribute__((always_inline)) का उपयोग करते समय संकलक नहीं चाहता है) है

+0

+1, उस उत्तर के लिए आपको धन्यवाद आदमी धन्यवाद। –

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

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