मुझे यह कहकर शुरू करना चाहिए कि सी और सी ++ पहली प्रोग्रामिंग भाषाएं थीं जो मैंने सीखीं। मैंने सी के साथ शुरुआत की, फिर स्कूल में सी ++, बहुत कुछ किया, और फिर सी में वापस धाराप्रवाह बनने के लिए चला गया।
सी सीखने पर पहली बात ने मुझे संकेत देने वालों को भ्रमित किया:
char ch;
char str[100];
scanf("%c %s", &ch, str);
यह भ्रम ज्यादातर जड़ में था क्योंकि मुझे संकेत ठीक से पेश किए जाने से पहले OUT तर्कों के लिए एक चर के संदर्भ का उपयोग करने के लिए पेश किया गया था। मुझे याद है कि मैं में पहले कुछ उदाहरण लिख छोड़ी सी के लिए Dummies क्योंकि वे बहुत सरल केवल करने के लिए कभी नहीं पहला कार्यक्रम मैं (सबसे अधिक संभावना है, क्योंकि इस की) काम करने के लिए लिखा मिल रहे थे।
इस बारे में भ्रमित करने वाली बात यह थी कि &ch
वास्तव में इसका मतलब क्या था और str
इसकी आवश्यकता क्यों नहीं थी।
के बाद मैं इससे परिचित हो गया कि मुझे याद है कि डायनेमिक आवंटन के बारे में उलझन में है। मुझे कुछ बिंदु पर एहसास हुआ कि डेटा के लिए संकेत किसी प्रकार के गतिशील आवंटन के बिना अत्यंत उपयोगी नहीं थे, इसलिए मैंने कुछ ऐसा लिखा:
char * x = NULL;
if (y) {
char z[100];
x = z;
}
गतिशील रूप से कुछ स्थान आवंटित करने का प्रयास करें। यह काम नहीं किया। मुझे यकीन नहीं था कि यह काम करेगा, लेकिन मुझे नहीं पता था कि यह कैसे काम कर सकता है।
मुझे बाद में पता चला malloc
और new
, लेकिन वे वास्तव में मेरे लिए जादुई मेमोरी जनरेटर की तरह लग रहे थे। मुझे कुछ नहीं पता था कि वे कैसे काम कर सकते हैं।
कुछ समय बाद मुझे फिर से पढ़ाया जा रहा था (मैंने इसे पहले अपने दम पर सीखा था, लेकिन अब कक्षा में था) और मैंने पूछा कि यह कैसे हुड के नीचे काम करता है - जहां अलग-अलग चर संग्रहीत थे। मेरे प्रोफेसर ने कहा "स्टैक पर" और बहुत सारी चीजें मेरे लिए स्पष्ट हो गईं। मैंने पहले भी शब्द सुने थे और इससे पहले सॉफ्टवेयर स्टैक्स लागू किया था। मैंने दूसरों को "स्टैक" का संदर्भ बहुत पहले सुना था, लेकिन इसके बारे में भूल गया था।
इस समय के दौरान मुझे यह भी एहसास हुआ कि C में बहुआयामी सरणियों का उपयोग करना बहुत भ्रमित कर सकता है। मुझे पता था कि उन्होंने कैसे काम किया है, लेकिन वे सिर्फ इतना आसान थे कि मैं जब भी मैं उन्हें इस्तेमाल करने के आसपास काम करने की कोशिश करने का फैसला किया। मुझे लगता है कि यहां मुद्दा ज्यादातर वाक्यात्मक (विशेष रूप से कार्य करने या उन्हें वापस करने) से गुजर रहा था।
चूंकि मैं अगले साल के लिए स्कूल के लिए C ++ लिख रहा था या दो मुझे डेटा संरचनाओं के लिए पॉइंटर्स का उपयोग करने का बहुत अनुभव मिला। यहाँ मुझे मुसीबतों का एक नया सेट मिला - पॉइंटर्स मिलाते हुए। मेरे पास कई स्तर के पॉइंटर्स (चीजें जैसे node ***ptr;
) मुझे यात्रा करते हैं। मैं एक पॉइंटर को बार-बार गलत संख्या से हटाता हूं और अंततः यह पता लगाने का सहारा लेता *
हूं कि मुझे परीक्षण और त्रुटि के लिए कितने की आवश्यकता थी।
कुछ बिंदु पर मैंने सीखा कि कैसे एक कार्यक्रम का ढेर काम करता है (जैसे कि, लेकिन इतना अच्छा है कि यह अब मुझे रात में नहीं रखता है)। मुझे यह याद है कि यदि आप सूचक को कुछ बाइट से पहले malloc
देखते हैं कि एक निश्चित सिस्टम रिटर्न पर, आप देख सकते हैं कि वास्तव में कितना डेटा आवंटित किया गया था। मुझे एहसास हुआ कि कोड malloc
ओएस से अधिक मेमोरी के लिए पूछ सकता है और यह मेमोरी मेरी निष्पादन योग्य फ़ाइलों का हिस्सा नहीं थी। कैसे malloc
काम करता है का एक सभ्य काम करने का विचार वास्तव में उपयोगी है।
इसके तुरंत बाद मैंने एक असेंबली क्लास ली, जिसने मुझे ज्यादातर प्रोग्रामर के बारे में पॉइंटर्स के बारे में नहीं सिखाया। यह मुझे इस बारे में अधिक सोचने के लिए मिला कि मेरे कोड का किस विधानसभा में अनुवाद किया जा सकता है। मैंने हमेशा कुशल कोड लिखने की कोशिश की थी, लेकिन अब मेरे पास एक बेहतर विचार था कि कैसे।
मैंने एक-दो कक्षाएं भी लीं जहाँ मुझे कुछ लिस्प लिखना था । जब लिस्प लिख रहा था तो मैं दक्षता के साथ चिंतित नहीं था जैसा कि मैं सी में था। मुझे बहुत कम पता था कि इस कोड का अनुवाद अगर संकलित किया जा सकता है, लेकिन मुझे पता था कि यह बहुत सारे स्थानीय नामांकित प्रतीकों (चर) का उपयोग करने जैसा लगता था चीजें बहुत आसान है। कुछ बिंदु पर मैंने लिस्प में थोड़ा सा एवीएल ट्री रोटेशन कोड लिखा है, क्योंकि मुझे पॉइंटर मुद्दों के कारण C ++ में लिखने में बहुत कठिन समय था। मैंने महसूस किया कि जो मैंने सोचा था कि उसका अधिक स्थानीय चर, सी ++ में और कई अन्य कार्यक्रमों को लिखने की मेरी क्षमता में बाधा थी।
मैंने कंपाइलर्स क्लास भी ली। इस वर्ग में रहते हुए मैं उन्नत सामग्री से आगे निकल गया और स्थैतिक एकल असाइनमेंट (एसएसए) और मृत चर के बारे में सीखा , जो महत्वपूर्ण नहीं है सिवाय इसके कि यह मुझे सिखाता है कि कोई भी सभ्य संकलक उन चर से निपटने का एक अच्छा काम करेगा जो हैं अब इस्तेमाल नहीं किया जाता। मुझे पहले से ही पता था कि सही प्रकार और अच्छे नामों के साथ अधिक चर (बिंदु सहित) मुझे चीजों को सीधे मेरे सिर में रखने में मदद करेंगे, लेकिन अब मैं यह भी जानता था कि दक्षता कारणों से उनसे बचना मेरे कम सूक्ष्म अनुकूलन दिमाग वाले प्रोफेसरों की तुलना में और भी अधिक बेवकूफ था। मुझे।
इसलिए मेरे लिए, एक कार्यक्रम के मेमोरी लेआउट के बारे में बहुत कुछ जानने से बहुत मदद मिली। यह सोचने के बारे में कि मेरे कोड का क्या अर्थ है, दोनों प्रतीकात्मक रूप से और हार्डवेयर पर, मेरी मदद करता है। सही प्रकार के स्थानीय पॉइंटर्स का उपयोग करने से बहुत मदद मिलती है। मैं अक्सर ऐसा कोड लिखता हूं जो दिखता है:
int foo(struct frog * f, int x, int y) {
struct leg * g = f->left_leg;
struct toe * t = g->big_toe;
process(t);
इतना है कि अगर मैं एक पॉइंटर प्रकार को पेंच करता हूं तो यह कंपाइलर त्रुटि से बहुत स्पष्ट है कि समस्या क्या है। अगर मैंने किया:
int foo(struct frog * f, int x, int y) {
process(f->left_leg->big_toe);
और वहां किसी भी प्रकार के पॉइंटर को गलत पाया गया, संकलक त्रुटि को पता लगाने के लिए पूरी तरह से अधिक कठिन होगा। मुझे अपनी हताशा में परीक्षण और त्रुटि परिवर्तनों का सहारा लेना होगा, और शायद चीजें बदतर हो जाएंगी।