केवल सी-स्ट्रिंग शाब्दिक ही क्यों पढ़े जाते हैं?


29

स्ट्रिंग-लिटरल रीड-ओनली (-इस / -इड) का क्या फायदा (ओं):

  1. फिर भी पैर में खुद को गोली मारने का एक और तरीका

    char *foo = "bar";
    foo[0] = 'd'; /* SEGFAULT */
    
  2. एक पंक्ति में शब्दों के पढ़ने-लिखने वाले सरणी को सुरुचिपूर्ण ढंग से आरंभ करने में असमर्थता:

    char *foo[] = { "bar", "baz", "running out of traditional placeholder names" };
    foo[1][2] = 'n'; /* SEGFAULT */ 
    
  3. भाषा की ही शिकायत करना।

    char *foo = "bar";
    char var[] = "baz";
    some_func(foo); /* VERY DANGEROUS! */
    some_func(var); /* LESS DANGEROUS! */
    

स्मृति को सहेज रहा है? मैंने कहीं पढ़ा है (अब स्रोत नहीं पा रहा है) कि लंबे समय पहले, जब रैम दुर्लभ था, कंपाइलरों ने इसी तरह के तारों को मर्ज करके मेमोरी उपयोग का अनुकूलन करने की कोशिश की।

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

सुसंगति के मुद्दे? मुझे लगता है कि एक विरासत कार्यक्रम जो केवल-पढ़ने की स्मृति तक पहुँचने की कोशिश करेगा या तो अनदेखा कर देगा या अनदेखे बग के साथ जारी रहेगा। इस प्रकार किसी भी विरासत कार्यक्रम को स्ट्रिंग शाब्दिक तक पहुँचने की कोशिश नहीं करनी चाहिए और वहाँ स्ट्रिंग शाब्दिक को लिखने की अनुमति देना वैध, गैर-हैकिश, पोर्टेबल विरासत कार्यक्रमों को नुकसान नहीं पहुंचाएगा ।

क्या कोई अन्य कारण हैं? क्या मेरा तर्क गलत है? क्या नए सी मानकों में स्ट्रिंग-राइटल्स पढ़ने-लिखने में बदलाव पर विचार करना उचित होगा या कम से कम कंपाइलर का विकल्प जोड़ा जाएगा? क्या यह पहले माना जाता था या मेरी "समस्याएं" बहुत छोटी और किसी को परेशान करने के लिए महत्वहीन थीं?


12
मुझे लगता है कि आपने देखा है कि कैसे संकलित कोड में स्ट्रिंग शाब्दिक दिखते हैं ?

2
विधानसभा को देखें कि मैंने जो लिंक प्रदान किया है, उसमें यह शामिल है। वहीं है।

8
आपका "अधिक" उदाहरण शून्य समाप्ति के कारण काम नहीं करेगा।
dan04

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

1
'स्ट्रिंग स्ट्रिंग को प्रोग्राम मेमोरी में स्टोर किया जाता है, रैम में नहीं, और बफर ओवरफ्लो से प्रोग्राम का भ्रष्टाचार ख़त्म हो जाता है?' रैम में भी प्रोग्राम इमेज है। सटीक होने के लिए, स्ट्रिंग शाब्दिक रैम को उसी खंड में संग्रहीत किया जाता है जिसका उपयोग प्रोग्राम की छवि को संग्रहीत करने के लिए किया जाता है। और हाँ, स्ट्रिंग को ओवरराइट करने से प्रोग्राम दूषित हो सकता है। MS-DOS और CP / M के दिनों में मेमोरी की सुरक्षा नहीं थी, आप इस तरह से सामान बना सकते थे, और यह आमतौर पर भयानक समस्या पैदा करता था। पहले पीसी वायरस आपके प्रोग्राम को संशोधित करने के लिए इस तरह ट्रिक्स का उपयोग करेंगे ताकि जब आप इसे चलाने की कोशिश करें तो यह आपकी हार्ड ड्राइव को स्वरूपित कर दे।
चार्ल्स ई। ग्रांट

जवाबों:


40

ऐतिहासिक रूप से (शायद इसके कुछ हिस्सों को फिर से लिखना), यह इसके विपरीत था। 1970 के दशक के पहले कंप्यूटरों पर (शायद PDP-11 ) एक प्रोटोटाइप भ्रूण सी (शायद BCPL ) चल रहा था, कोई MMU और कोई मेमोरी प्रोटेक्शन नहीं था (जो कि ज्यादातर पुराने IBM / 360 मेनफ्रेम पर मौजूद था )। तो स्मृति के हर बाइट (शाब्दिक तार या मशीन कोड से निपटने वाले भी) एक गलत कार्यक्रम द्वारा ओवरराइट किया जा सकता है (एक कार्यक्रम कुछ बदल रहा है की कल्पना %करने के लिए /एक में (3) printf प्रारूप स्ट्रिंग)। इसलिए, शाब्दिक तार और स्थिरांक लेखन योग्य थे।

1975 में एक किशोरी के रूप में, मैंने पेरिस में पुराने 1960 के दशक के पुराने कंप्यूटरों पर मेमोरी प्रोटेक्शन के बिना पलैस डे ला डेकोवरटे म्यूजियम में कोडिंग की: आईबीएम / 1620 में केवल एक कोर मेमोरी थी, जिसे आप कीबोर्ड के माध्यम से शुरू कर सकते थे, इसलिए उसे कई दर्जनों टाइप करना पड़ा अंकित टेप पर प्रारंभिक कार्यक्रम को पढ़ने के लिए अंकों का; सीएबी / 500 में एक चुंबकीय ड्रम मेमोरी थी; आप ड्रम के पास यांत्रिक स्विच के माध्यम से कुछ ट्रैक लिखने को अक्षम कर सकते हैं।

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

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

तो सी मानक बैरोक है: कानूनी रूप से (केवल ऐतिहासिक कारणों के लिए), शाब्दिक तार const char[]सरणियाँ नहीं हैं, लेकिन केवल char[]सरणियाँ जिन्हें ओवरराइट करने की मनाही है।

BTW, कुछ मौजूदा भाषाओं में स्ट्रिंग लिटरल को अधिलेखित करने की अनुमति है (यहां तक ​​कि Ocaml जो कि ऐतिहासिक रूप से-और बुरी तरह से गलत लेखन योग्य है) ने हाल ही में 4.02 में उस व्यवहार को बदल दिया है, और अब केवल-पढ़ने के लिए स्ट्रिंग है)।

वर्तमान सी compilers का अनुकूलन और है करने में सक्षम हैं "ions"और "expressions"अपने पिछले 5 (समाप्त अशक्त बाइट सहित) बाइट्स को साझा करें।

फाइल में अपनी सी कोड को संकलित करने का प्रयास foo.cके साथ gcc -O -fverbose-asm -S foo.cउत्पन्न कोडांतरक फाइल के अंदर और देखो foo.sद्वारा जीसीसी

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

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

आईबीएम / 7094 पर पुराने फोरट्रान77 दिनों में, एक बग भी एक निरंतरता को बदल सकता है: यदि आप CALL FOO(1)और यदि FOOइसके तर्क को 2 के संदर्भ में पारित करने के लिए संशोधित किया गया है, तो कार्यान्वयन 1 के 2 में अन्य घटनाओं को बदल सकता है, और यह वास्तव में था शरारती बग, खोजने के लिए काफी मुश्किल है।


क्या यह स्थिरांक के रूप में तारों की रक्षा के लिए है? भले ही वे constमानक ( stackoverflow.com/questions/2245664/… ) के रूप में परिभाषित नहीं हैं ?
४aus पर मारियस मैकजॉस्कस २ '

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

1
एक संग्रहालय में एक किशोर के रूप में, मैंने 1975 में पुराने IBM / 1620 और CAB500 कंप्यूटर पर कोड किया। न तो कोई ROM था: IBM / 1620 में कोर मेमोरी थी, और CAB500 में एक मैग्नेटिक ड्रम था (कुछ ट्रैक्स को मैकेनिकल स्विच द्वारा लिखने योग्य होने के लिए अक्षम किया जा सकता था)
Basile Starynkevitch

2
यह भी इंगित करने के लायक है: कोड सेगमेंट में शाब्दिक रूप से पुट करने का मतलब है कि उन्हें कार्यक्रम की कई प्रतियों के बीच साझा किया जा सकता है क्योंकि प्रारंभिक समय रन के बजाय संकलन समय पर होता है।
Blrfl

@ डेडप्लिकेटर खैर, मैंने एक ऐसी मशीन देखी है जिसमें एक बेसिक वैरिएंट चल रहा है जिसने आपको पूर्णांक स्थिरांक बदलने की अनुमति दी है (मुझे यकीन नहीं है कि आपको ऐसा करने के लिए ट्रिक करने की ज़रूरत है, उदाहरण के लिए "बायफ़र" दलीलें या यदि एक साधारण let 2 = 3काम कर रहा है)। इसके परिणामस्वरूप बहुत से FUN (शब्द के बौने किले की परिभाषा में), ज़ाहिर है। मुझे पता नहीं है कि दुभाषिया को कैसे डिजाइन किया गया था कि उसने इसकी अनुमति दी, लेकिन यह था।
लुआं

2

कंपाइलर संयोजन नहीं कर सकते हैं "more"और "regex", क्योंकि पूर्व में एक अशक्त बाइट है eजबकि बाद में एक है x, लेकिन कई कंपाइलर स्ट्रिंग शाब्दिकों को मिलाएंगे जो पूरी तरह से मेल खाते हैं, और कुछ स्ट्रिंग स्ट्रिंग शाब्दिकों से भी मेल खाते हैं जो एक आम पूंछ साझा करते हैं। कोड जो एक स्ट्रिंग शाब्दिक को बदलता है, इस प्रकार एक अलग स्ट्रिंग शाब्दिक बदल सकता है जो कुछ पूरी तरह से अलग उद्देश्य के लिए उपयोग किया जाता है लेकिन इसमें समान वर्ण होते हैं।

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

float sum(float *f1, float *f2) { return *f1 + *f2; }

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.