लीगेसी सी कोड को समझना

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

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

प्रभावी रिफैक्टरिंग के लिए रणनीतियाँ

निम्नलिखित रणनीतियों को विरासत सी कोड को आधुनिक बनाने के लिए एक व्यवस्थित ढांचा तैयार किया गया है। सॉफ्टवेयर की मुख्य कार्यक्षमता को संरक्षित करते समय प्रत्येक दृष्टिकोण तकनीकी ऋण को कम करता है।

1. एक व्यापक कोड लेखा परीक्षा का संचालन

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

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

2. आधुनिक कोडिंग मानकों की स्थापना

कोडबेस में स्थिरता लाने के लिए एक मान्यता प्राप्त कोडिंग मानक को अपनाने। MISRA C दिशानिर्देश] (आमतौर पर मोटर वाहन और सुरक्षा-महत्वपूर्ण प्रणालियों में इस्तेमाल किया) अपरिभाषित व्यवहार को कम करने और पठनीयता में सुधार करने के लिए। सामान्य प्रयोजन परियोजनाओं के लिए, नवीनतम C मानक का पालन करें-कम से कम C11, अधिमानतः C17। यह ], अनाम संरचनाओं और धागे (C11) जैसी सुविधाओं तक पहुंच प्रदान करता है।

कार्यों और चर के लिए नामकरण सम्मेलनों को मानकीकृत करें (उदाहरण के लिए, ]], मैक्रोज़ के लिए]), इंडेंटेशन (टैब बनाम स्पेस), और टिप्पणी शैली (Doxygen या इसी तरह)। अपने निरंतर एकीकरण पाइपलाइन में एक linter जैसे clang-tidy] के माध्यम से इन नियमों को लागू करें।

3. कोड को मॉड्यूलर करें

विरासत C अक्सर एकरसीय कार्य करता है जो सैकड़ों या हजारों लाइनों को फैलाता है। उन्हें छोटे, एकजुट कार्यों में तोड़ दें जो प्रत्येक एक चीज करते हैं। कार्यान्वयन के लिए सार्वजनिक इंटरफेस और स्रोत फ़ाइलों को घोषित करने के लिए हेडर फ़ाइलों का उपयोग करें। उदाहरण के लिए, एक फ़ाइल को विभाजित करें जो नेटवर्किंग और फ़ाइल दोनों को अलग मॉड्यूल में I/O ] / ] और ]]].

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

// Before: monolithic, global state
int buffer[256];
int index = 0;
void process_data() { /* manipulates global buffer and index */ }

// After: encapsulated module
// buffer.h
typedef struct Buffer Buffer;
Buffer* buffer_create(size_t size);
int buffer_push(Buffer* b, int value);
void buffer_destroy(Buffer* b);

// buffer.c
struct Buffer {
 int* data;
 size_t size;
 size_t index;
};
Buffer* buffer_create(size_t size) { ... }

4. Deprecated और Unsafe कार्यों को बदल दें

C मानक पुस्तकालय में कई कुख्यात असुरक्षित कार्य होते हैं जो या तो आधुनिक सुरक्षित कोडिंग में विस्थापित या हतोत्साहित होते हैं। उन्हें व्यवस्थित रूप से बदलें:

  • ] → ]]]
  • ] → ]]] ]]]]
  • ] → ]] ]]]]
  • ] → ]]]]
  • [[FLT::21]]] → ]]]
  • ] → ]]]]
  • ] → ]] + ]]]] फ़ील्ड चौड़ाई सीमा के साथ

ये परिवर्तन बफर ओवरफ्लो को समाप्त करते हैं, सुरक्षा भेद्यता का एक प्रमुख स्रोत। इसके अतिरिक्त, विंडोज पर को परिभाषित करके पुराने कार्यों को अक्षम करें या कम्पाइलर झंडे का उपयोग करके जो त्रुटियों के रूप में विकृत कार्यों का इलाज करते हैं। SEI CERT C Coding Standard] सुरक्षित विकल्पों की एक व्यापक सूची प्रदान करता है।

5. मेमोरी प्रबंधन में सुधार

विरासत C में गतिशील स्मृति आवंटन अक्सर त्रुटि-प्रवण होता है। आम मुद्दों में मुक्त स्मृति, डबल मुक्त और dangling सूचकों को भूल जाना शामिल है। इन प्रथाओं के साथ Refactor स्मृति प्रबंधन:

  • [[FLT::29]]]] का प्रयोग ] के बजाय जब शून्य-इन्टिलाइज़्ड मेमोरी की आवश्यकता होती है।
  • हमेशा ] के लिए आवंटन कार्यों के रिटर्न मान की जांच करें।
  • रैपर फंक्शन्स बनाएं जो आवंटन (जैसे, को ट्रैक करें, जो विफलता पर गर्भपात करती है)।
  • एक सुसंगत स्वामित्व मॉडल को अपनाने: दस्तावेज़ जो कार्य स्मृति का मालिक है और इसे मुक्त करने के लिए जिम्मेदार है।
  • ]]Valgrind (Memcheck) या पताSanitizer (ASan) जैसे उपकरणों का उपयोग परीक्षण के दौरान लीक और आउट-ऑफ-बाउंड एक्सेस का पता लगाने के लिए किया जाता है।

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

6. सुरक्षित सूचक उपयोग को अपनाने

पॉइंटर्स एक डबल एज्ड तलवार हैं। बग्स की संभावना को कम करने के लिए उनके उपयोग को आधुनिक बनाना:

  • ] का प्रयोग उन फंक्शन मापदंडों के लिए किया जाता है जो संशोधित नहीं होते हैं। इससे अनुबंध को स्पष्ट कर दिया जाता है और कम्पाइलर को अनुकूलन करने में मदद मिलती है।
  • उन वस्तुओं को क्विलीफाई करें जो (C99 के आगे) के साथ नहीं हैं। यह बेहतर वेक्टरीकरण को सक्षम बनाता है।
  • कास्टिंग से बचें ] अनावश्यक रूप से. जब एक बाइट स्ट्रीम से रीडिंग करते हैं, तो सख्त आह्वान उल्लंघन से बचने के लिए कास्टिंग के बजाय का उपयोग करें।
  • अनिर्धारित व्यवहार को रोकने के लिए ठीक से टाइप किए गए फंक्शन पॉइंटर्स के साथ फंक्शन पॉइंटर कास्ट को बदलें।
  • ] के बजाय लचीला सरणी सदस्यों (C99) का उपयोग करें (अनुदेश के अंत में आकार की सरणी)।
// Avoid: casting void* to misaligned type
int value = *(int*)(byte_buffer + offset); // potential UB

// Prefer: memcpy
int value;
memcpy(&value, byte_buffer + offset, sizeof(value));

7. त्रुटि हैंडलिंग में सुधार

विरासत C अक्सर ], रिटर्न कोड और वैश्विक त्रुटि राज्यों के मिश्रण का उपयोग करता है। एक सुसंगत पैटर्न में त्रुटि हैंडलिंग को एकीकृत करें। विकल्पों में शामिल हैं:

  • कार्यों के लिए एन्युमेटेड रिटर्न प्रकार का उपयोग करें (उदाहरण के लिए, ]).
  • त्रुटि कोड के लिए लौटने से बचें; हस्ताक्षर किए गए पूर्णांक त्रुटियों के लिए नकारात्मक मूल्यों की अनुमति देते हैं।
  • जटिल प्रणालियों के लिए, /]] का उपयोग करके हल्के अपवाद-हैंडलिंग पैटर्न को लागू करें (लेकिन स्पारिंगली का उपयोग करें, क्योंकि वे प्रवाह नियंत्रण को जटिल बनाते हैं)।
  • एक उच्च स्तर पर त्रुटियां लॉग करें और दोहराए जाने वाले क्लीनअप कोड से बचने के लिए ] पैटर्न (न्यायिक रूप से) का उपयोग करके आवंटित संसाधनों को साफ़ रूप से खोलना।

8. परिचय इकाई परीक्षण

परीक्षण के बिना, रिफैक्टरिंग को तेज करना है। जल्दी एक यूनिट परीक्षण ढांचे को स्थापित करें। C के लिए लोकप्रिय विकल्प शामिल हैं:

  • Unity - हल्के, एम्बेडेड सिस्टम के लिए आदर्श।
  • CMocka] – में मॉड्यूल को अलग करने के लिए नकली समर्थन शामिल है।
  • Cunit - पारंपरिक लेकिन कार्यात्मक।

प्रत्येक रिफैक्टर मॉड्यूल के लिए यूनिट परीक्षण लिखें। परीक्षण-संचालित विकास (TDD) का उपयोग करें जहां व्यवहार्य: वांछित व्यवहार को परिभाषित करने वाले परीक्षण को लिखने के लिए, फिर परीक्षण पास होने तक रिफैक्टर। एकीकरण परीक्षण को ज्ञात इनपुट और अपेक्षित आउटपुट के साथ पूरे सिस्टम को चलाने चाहिए। तुरंत रिग्रेशन को पकड़ने के लिए एक CI वातावरण में सभी परीक्षणों को स्वचालित करें।

9. प्रदर्शन विचार

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

परीक्षण और सत्यापन

एक चरणबद्ध परीक्षण रणनीति तब महत्वपूर्ण है जब विरासत कोड को फिर से तैयार किया जाता है। इन चरणों का पालन करें:

  1. Regression test – आधार रेखा स्थापित करने के लिए परिवर्तन करने से पहले मौजूदा परीक्षण सूट (यदि कोई हो) चलाएं। यदि कोई परीक्षण मौजूद नहीं है, तो धूम्रपान परीक्षण लिखें जो कोर मार्गों का प्रयोग करते हैं।
  2. ]Incremental सत्यापन [ - एक समय में एक मॉड्यूल को रिफैक्टर करें। प्रत्येक परिवर्तन के बाद, सख्त झंडे और यूनिट परीक्षण चलाने के साथ संकलित करें। छोटे, परमाणु के साथ संस्करण नियंत्रण (जैसे, गिट) का उपयोग करें ताकि आप आसानी से वापस लौट सकें।
  3. Static विश्लेषण एकीकरण – अपने CI पाइपलाइन के लिए Cppcheck और clang-tidy जोड़ें। गुणवत्ता को लागू करने के लिए त्रुटियों के रूप में चेतावनी का इलाज करें।
  4. Dynamic विश्लेषण - रात के दौरान वैलग्रिड या एएसन के तहत भागो, जो पुनर्निर्माण द्वारा पेश की गई स्मृति मुद्दों का पता लगाने के लिए तैयार है।
  5. User स्वीकृति परीक्षण - एक मंचन वातावरण में पुनर्निर्मित प्रणाली को तैनात करें और डोमेन विशेषज्ञों को अंतिम-टू-एंड परीक्षण करने का प्रयास करें। मूल के साथ आउटपुट लॉग, टाइमिंग और संसाधन उपयोग की तुलना करें।

एक CI सर्वर (GitHub एक्शन, जेनकिंस, गिटलै सीआई) के साथ इन चरणों को स्वचालित रूप से मैन्युअल ओवरहेड को कम कर देता है और रिफैक्टरिंग प्रक्रिया में विश्वास पैदा करता है।

निष्कर्ष

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