प्रकार हमेशा एक निश्चित आकार के क्यों होते हैं, कोई फर्क नहीं पड़ता?


149

कार्यान्वयन विभिन्न प्रकार के वास्तविक आकारों के बीच भिन्न हो सकते हैं, लेकिन अधिकांश पर, अहस्ताक्षरित इंट और फ्लोट जैसे प्रकार हमेशा 4 बाइट्स होते हैं। लेकिन क्यों एक प्रकार की स्मृति हमेशा एक निश्चित मात्रा में होती है, चाहे उसका मूल्य कितना भी क्यों न हो? उदाहरण के लिए, यदि मैंने 255 के मान के साथ निम्नलिखित पूर्णांक बनाया है

int myInt = 255;

फिर myIntमेरे संकलक के साथ 4 बाइट पर कब्जा होगा। हालांकि, वास्तविक मूल्य, 255केवल 1 बाइट के साथ दर्शाया जा सकता है, इसलिए myIntस्मृति के 1 बाइट पर कब्जा क्यों नहीं किया जाएगा ? या पूछने का अधिक सामान्यीकृत तरीका: किसी प्रकार का केवल एक ही आकार क्यों होता है जब मूल्य को दर्शाने के लिए आवश्यक स्थान उस आकार से छोटा हो सकता है?


15
1) " हालांकि, वास्तविक मूल्य, 256 को केवल 1 बाइट के साथ दर्शाया जा सकता है " गलत, सबसे बड़ा unsingedमूल्य, जिसे 1 बाइट के साथ दर्शाया जा सकता है 255। 2) मूल्य परिवर्तन के रूप में, एक चर के इष्टतम भंडारण आकार की गणना के ओवरहेड, और भंडारण क्षेत्र का विस्तार / विस्तार करना।
अल्गिरदास प्रीदिज़ियस

99
खैर, जब स्मृति से मूल्य पढ़ने का समय आता है , तो आप मशीन का प्रस्ताव कैसे निर्धारित करेंगे कि कितने बाइट्स पढ़ने के लिए हैं? मशीन को कैसे पता चलेगा कि मूल्य पढ़ना कहां बंद करना है? इसके लिए अतिरिक्त सुविधाओं की आवश्यकता होगी। और सामान्य स्थिति में इन अतिरिक्त सुविधाओं के लिए मेमोरी और प्रदर्शन ओवरहेड केवल unsigned intमूल्य के लिए निर्धारित 4 बाइट्स का उपयोग करने की तुलना में बहुत अधिक होगा ।
चींटी

74
मुझे वास्तव में यह सवाल पसंद है। भले ही इसका जवाब देना सरल लग रहा हो, लेकिन मुझे लगता है कि एक सटीक विवरण देने के लिए एक अच्छी समझ की आवश्यकता है कि कंप्यूटर और कंप्यूटर आर्किटेक्चर वास्तव में कैसे काम करते हैं। अधिकांश लोग संभवतः इसके लिए एक व्यापक स्पष्टीकरण के बिना, बस इसे प्रदान करने के लिए ले जाएंगे।
andree

37
विचार करें कि क्या होगा यदि आपने चर के मूल्य में 1 जोड़ दिया, जिससे यह 256 हो गया, इसलिए इसे विस्तार करने की आवश्यकता होगी। इसका विस्तार कहां तक ​​है? क्या आप अंतरिक्ष बनाने के लिए बाकी मेमोरी को स्थानांतरित करते हैं? क्या चर ही चलता है? यदि ऐसा होता है, तो यह कहां स्थानांतरित होता है और आप कैसे इंगित करते हैं कि आपको अपडेट करने की आवश्यकता है?
मोलबेडनिलो

13
@ सिओमीडॉट नप, आप गलत हैं। std::vector<X>हमेशा एक ही आकार होता है, अर्थात sizeof(std::vector<X>)एक संकलन-समय स्थिर होता है।
सर्गेई

जवाबों:


131

संकलक को किसी मशीन के लिए कोडांतरक (और अंततः मशीन कोड) का उत्पादन करना चाहिए, और आमतौर पर C ++ उस मशीन के लिए सहानुभूति रखने की कोशिश करता है।

अंतर्निहित मशीन के लिए सहानुभूति होने का अर्थ है मोटे तौर पर: सी ++ कोड लिखना आसान बनाता है जो मशीन के संचालन पर कुशलता से मैप कर सकता है। इसलिए, हम अपने हार्डवेयर प्लेटफ़ॉर्म पर तेज़ और "प्राकृतिक" डेटा प्रकार और संचालन तक पहुँच प्रदान करना चाहते हैं।

लगातार, एक विशिष्ट मशीन वास्तुकला पर विचार करें। आइए वर्तमान इंटेल x86 परिवार को लेते हैं।

Intel® 64 और IA-32 आर्किटेक्चर सॉफ्टवेयर डेवलपर मैनुअल 1 ( लिंक ), खंड 3.4.1 कहता है:

32-बिट सामान्य-उद्देश्य रजिस्टर में EAX, EBX, ECX, EDX, ESI, EDI, EBP और ESP निम्नलिखित आइटम रखने के लिए दिए गए हैं:

• तार्किक और अंकगणितीय परिचालनों के लिए प्रचालन करता है

• पता गणना के लिए परिचालनों

• मेमोरी पॉइंटर्स

इसलिए, हम चाहते हैं कि कंपाइलर इन EAX, EBX आदि का उपयोग करें, जब यह सरल C ++ पूर्णांक अंकगणित को संकलित करता है। इसका मतलब यह है कि जब मैं एक की घोषणा करता हूं int, तो यह इन रजिस्टरों के साथ संगत होना चाहिए, ताकि मैं उन्हें कुशलतापूर्वक उपयोग कर सकूं।

रजिस्टर हमेशा एक ही आकार के होते हैं (यहां, 32 बिट्स), इसलिए मेरे intचर हमेशा 32 बिट्स भी होंगे। मैं उसी लेआउट (लिटिल-एंडियन) का उपयोग करूंगा, ताकि मुझे हर बार एक रूपांतरण को एक रजिस्टर में लोड करने, या एक रजिस्टर को एक चर में संग्रहीत करने की आवश्यकता न हो।

Godbolt का उपयोग करके हम देख सकते हैं कि संकलक कुछ तुच्छ कोड के लिए क्या करता है:

int square(int num) {
    return num * num;
}

संकलन (जीसीसी 8.1 और -fomit-frame-pointer -O3सादगी के लिए) के साथ:

square(int):
  imul edi, edi
  mov eax, edi
  ret

इसका मतलब है की:

  1. int numपैरामीटर रजिस्टर ईडीआई में पारित कर दिया है, जिसका अर्थ यह बिल्कुल आकार है और इंटेल एक देशी रजिस्टर के लिए उम्मीद लेआउट था। फ़ंक्शन को कुछ भी परिवर्तित करने की आवश्यकता नहीं है
  2. गुणन एक एकल निर्देश ( imul) है, जो बहुत तेज़ है
  3. परिणाम वापस करना बस इसे दूसरे रजिस्टर में कॉपी करने की बात है (कॉलर को परिणाम EAX में डालने की उम्मीद है)

संपादित करें: हम एक गैर-देशी लेआउट का उपयोग करके अंतर दिखाने के लिए एक प्रासंगिक तुलना जोड़ सकते हैं। सबसे सरल मामला मूल चौड़ाई के अलावा किसी अन्य चीज़ में मूल्यों को संग्रहीत करना है।

फिर से गॉडबोल्ट का उपयोग करके , हम एक साधारण देशी गुणन की तुलना कर सकते हैं

unsigned mult (unsigned x, unsigned y)
{
    return x*y;
}

mult(unsigned int, unsigned int):
  mov eax, edi
  imul eax, esi
  ret

गैर-मानक चौड़ाई के लिए समान कोड के साथ

struct pair {
    unsigned x : 31;
    unsigned y : 31;
};

unsigned mult (pair p)
{
    return p.x*p.y;
}

mult(pair):
  mov eax, edi
  shr rdi, 32
  and eax, 2147483647
  and edi, 2147483647
  imul eax, edi
  ret

सभी अतिरिक्त निर्देश इनपुट प्रारूप (दो 31-बिट अहस्ताक्षरित पूर्णांक) को उस प्रारूप में परिवर्तित करने से संबंधित हैं जो प्रोसेसर मूल रूप से संभाल सकता है। यदि हम परिणाम को वापस 31-बिट मान में संग्रहीत करना चाहते हैं, तो ऐसा करने के लिए एक या दो निर्देश होंगे।

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


गतिशील आकारों पर एक नोट:

उपरोक्त उदाहरण अभी भी चर-चौड़ाई के बजाय निश्चित-चौड़ाई मान है, लेकिन चौड़ाई (और संरेखण) अब मूल रजिस्टरों से मेल नहीं खाते हैं।

X86 प्लेटफ़ॉर्म में कई मूल आकार हैं, जिनमें मुख्य 32-बिट के अलावा 8-बिट और 16-बिट शामिल हैं (मैं 64-बिट मोड पर चमक रहा हूं और सादगी के लिए अन्य कई चीजें)।

ये प्रकार (चार, int8_t, uint8_t, int16_t आदि) भी सीधे वास्तुकला द्वारा समर्थित हैं - आंशिक रूप से पुराने 8086/286/386 / आदि के साथ पिछड़े संगतता के लिए। आदि निर्देश सेट।

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

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


दक्षता पर एक और ध्यान दें

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

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

इसके लिए आप जिस पेनल्टी का भुगतान करते हैं उसका मतलब है कि यह तभी सार्थक है जब आपको बैंडविड्थ या दीर्घकालिक भंडारण को पूरी तरह से कम करने की आवश्यकता होती है, और उन मामलों के लिए जिन्हें आमतौर पर एक सरल और प्राकृतिक प्रारूप का उपयोग करना आसान होता है - और फिर इसे एक सामान्य-उद्देश्य प्रणाली के साथ संपीड़ित करें। (जैसे ज़िप, gzip, bzip2, xy या जो भी हो)।


tl; डॉ

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


मैं उन सभी की समझ बनाने की कोशिश करते हुए सभी अच्छे उत्तरों को देख रहा हूं .. इसलिए आपके उत्तर के संबंध में, एक डायनामिक आकार नहीं होगा, पूर्णांक के लिए 32 बिट्स से कम कहें, न कि केवल एक रजिस्टर के भीतर अधिक चर के लिए अनुमति दें ? अगर धीरज वही है, तो यह इष्टतम क्यों नहीं होगा?
निकलैस उडेन

7
@dd लेकिन आप कितने रजिस्टर में उपयोग करेंगे जो यह पता लगाते हैं कि वर्तमान में कितने चर एक रजिस्टर में संग्रहीत हैं?
user253751

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

3
मैं वास्तव में तो है कुछ मूल्य के लिए सभी 32 बिट की जरूरत है, मैं अभी भी कहीं न कहीं लंबाई स्टोर करने के लिए तो अब मैं की जरूरत की जरूरत है, की तुलना में अधिक कुछ मामलों में 32 बिट।
बेकार

1
+1। "सरल और प्राकृतिक प्रारूप और फिर संपीड़ित" के बारे में एक नोट आम तौर पर बेहतर होता है: यह निश्चित रूप से सच है , लेकिन : कुछ डेटा के लिए VLQ-प्रत्येक-मान-तब-सेक-द-द-चीज़ पूरी तरह से संपीड़ित-से बेहतर प्रदर्शन करता है -जबकि, और कुछ अनुप्रयोगों के लिए, आपके डेटा को एक साथ संपीड़ित नहीं किया जा सकता है , क्योंकि यह या तो असमान है (जैसा कि git'मेटाडेटा') या आप वास्तव में इसे याद रख रहे हैं, कभी-कभी इसे बेतरतीब ढंग से एक्सेस या संशोधित करने की आवश्यकता होती है, लेकिन अधिकांश नहीं मान (HTML + CSS रेंडरिंग इंजन के रूप में), और इस तरह केवल VLQ जैसी जगह का उपयोग करके ही हिलाया जा सकता है।
मृत्युंजय

139

क्योंकि प्रकार मौलिक रूप से भंडारण का प्रतिनिधित्व करते हैं, और वे अधिकतम मूल्य के संदर्भ में परिभाषित किए जाते हैं जो वे पकड़ सकते हैं, वर्तमान मूल्य नहीं।

बहुत सरल सादृश्य एक घर होगा - एक घर का एक निश्चित आकार होता है, भले ही इसमें कितने लोग रहते हों, और एक बिल्डिंग कोड भी होता है, जो अधिकतम संख्या में ऐसे लोगों को निर्धारित करता है जो एक निश्चित आकार के घर में रह सकते हैं।

हालांकि, भले ही एक व्यक्ति एक घर में रह रहा हो जो 10 को समायोजित कर सकता है, घर का आकार रहने वालों की वर्तमान संख्या से प्रभावित होने वाला नहीं है।


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

5
"क्योंकि प्रकार मौलिक रूप से भंडारण का प्रतिनिधित्व करते हैं" यह सभी भाषाओं के लिए सही नहीं है (उदाहरण के लिए, उदाहरण के लिए)
corvus_192

56
@ corvus_192 टैग का अर्थ है। इस प्रश्न को C ++ के साथ टैग किया गया है, 'टाइपस्क्रिप्ट' नहीं
सर्जिया

4
@ ahouse101 वास्तव में, ऐसी कई भाषाएँ हैं जिनमें असीमित-सटीक पूर्णांक हैं, वे आवश्यकतानुसार बढ़ती हैं। इन भाषाओं में आपको चर के लिए निश्चित मेमोरी आवंटित करने की आवश्यकता नहीं होती है, वे आंतरिक रूप से ऑब्जेक्ट संदर्भ के रूप में कार्यान्वित होते हैं। उदाहरण: लिस्प, पायथन।
बमर

2
@jamesqf यह संभवत: कोई संयोग नहीं है कि सांसद अंकगणित को पहली बार लिस्प में गले लगाया गया था, जिसने जादुई जादुई प्रबंधन भी किया था। डिजाइनरों ने महसूस किया कि प्रदर्शन प्रभाव प्रोग्रामिंग की आसानी के लिए माध्यमिक थे। और प्रभाव को कम करने के लिए अनुकूलन तकनीकों का विकास किया गया।
बरमार

44

यह एक अनुकूलन और सरलीकरण है।

आपके पास निश्चित आकार की वस्तुएं हो सकती हैं। इस प्रकार मूल्य का भंडारण।
या आपके पास वेरिएबल साइज ओब्जेक्ट्स हो सकते हैं। लेकिन भंडारण मूल्य और आकार।

निश्चित आकार की वस्तुएं

संख्या में हेरफेर करने वाले कोड को आकार के बारे में चिंता करने की आवश्यकता नहीं है। आप मानते हैं कि आप हमेशा 4 बाइट्स का उपयोग करते हैं और कोड को बहुत सरल बनाते हैं।

गतिशील आकार की वस्तुएं

किसी हेरफेर को पढ़ते समय कोड को हेरफेर संख्या को समझना चाहिए कि उसे मूल्य और आकार पढ़ना होगा। यह सुनिश्चित करने के लिए आकार का उपयोग करें कि सभी उच्च बिट्स रजिस्टर में शून्य हैं।

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

सारांश

निश्चित आकार की वस्तुओं के लिए उत्पन्न कोड बहुत सरल है।

ध्यान दें

संपीड़न इस तथ्य का उपयोग करता है कि 255 एक बाइट में फिट होगा। बड़े डेटा सेटों को संग्रहीत करने के लिए संपीड़न योजनाएं हैं जो सक्रिय रूप से विभिन्न संख्याओं के लिए विभिन्न आकार मानों का उपयोग करेंगे। लेकिन चूंकि यह लाइव डेटा नहीं है, इसलिए आपके पास ऊपर वर्णित जटिलताएं नहीं हैं। आप संग्रहण के लिए डेटा को संपीड़ित / डी-कंप्रेस करने की कीमत पर डेटा को संग्रहीत करने के लिए कम जगह का उपयोग करते हैं।


4
यह मेरे लिए सबसे अच्छा जवाब है: आप आकार का ट्रैक कैसे रखते हैं? अधिक स्मृति के साथ ?
ऑनलाइन थॉमस

@ThomasMoors हां, बिल्कुल: अधिक मेमोरी के साथ । यदि आप, उदाहरण के लिए एक गतिशील सरणी है, तो कुछ intउस सरणी में तत्वों की संख्या संग्रहीत करेंगे। वह intस्वयं एक निश्चित आकार फिर से होगा।
अल्फ

1
@ थोमसोमोर्स आमतौर पर उपयोग किए जाने वाले दो विकल्प होते हैं, जिनमें से दोनों को अतिरिक्त मेमोरी की आवश्यकता होती है - या तो आपके पास एक (निश्चित आकार) फ़ील्ड है जो आपको बताता है कि कितना डेटा है (उदाहरण के लिए सरणी आकार के लिए एक इंट, या "पास्कल-शैली" तार जहां पहले तत्व में कितने वर्ण हैं), या वैकल्पिक रूप से आपके पास एक श्रृंखला (या अधिक जटिल संरचना) हो सकती है, जहां प्रत्येक तत्व किसी भी तरह से नोट करता है यदि यह अंतिम एक है - जैसे शून्य-समाप्त तार, या लिंक किए गए सूचियों के अधिकांश रूप।
पीटरिस

27

क्योंकि C ++ जैसी भाषा में, एक डिज़ाइन लक्ष्य यह है कि सरल संचालन सरल मशीन निर्देशों के लिए संकलित होता है।

सभी मुख्य धारा सीपीयू अनुदेश सेट निश्चित-चौड़ाई प्रकारों के साथ काम करते हैं, और यदि आप चर-चौड़ाई प्रकार करना चाहते हैं, तो आपको उन्हें संभालने के लिए कई मशीन निर्देश करने होंगे।

के रूप में क्यों अंतर्निहित कंप्यूटर हार्डवेयर इस तरह से है: यह इसलिए है क्योंकि यह सरल है, और कई मामलों के लिए अधिक कुशल है (लेकिन सभी नहीं)।

कंप्यूटर को टेप के टुकड़े के रूप में कल्पना करें:

| xx | xx | xx | xx | xx | xx | xx | xx | xx | xx | xx | xx | xx | ...

यदि आप बस कंप्यूटर को टेप पर पहली बाइट को देखने के लिए कहते हैं, तो xx, यह कैसे पता चलेगा कि प्रकार वहाँ बंद हो जाता है, या अगले बाइट पर आगे बढ़ता है? यदि आपके पास 255(हेक्साडेसिमल FF) जैसा कोई नंबर या 65535(हेक्साडेसिमल FFFF) जैसा कोई नंबर है, तो पहला बाइट हमेशा होता है FF

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

C और C ++ जैसी भाषाओं की निश्चित-चौड़ाई प्रकार दर्शाती है।

यह इस तरह से नहीं होता है, और अधिक अमूर्त भाषाएं जो अधिकतम कुशल कोडिंग के लिए मैपिंग से कम चिंतित हैं, संख्यात्मक प्रकारों के लिए चर-चौड़ाई एन्कोडिंग (जिसे "परिवर्तनीय लंबाई मात्रा" या वीएलक्यू के रूप में भी जाना जाता है) का उपयोग करने के लिए स्वतंत्र हैं।

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


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

लेकिन , जब कोड को अन्य कोड के साथ इंटर-ऑपरेट करना पड़ता है जिसे अलग से संकलित किया जा सकता है, तो आकारों को लगातार रहना होगा, या यह सुनिश्चित करना होगा कि कोड का प्रत्येक टुकड़ा समान सम्मेलन का अनुसरण करता है।

क्योंकि अगर यह सुसंगत नहीं है, तो यह जटिलता है: क्या होगा अगर मेरे पास है, int x = 255;लेकिन बाद में कोड में है x = y? यदि intपरिवर्तनशील-चौड़ाई हो सकती है, तो संकलक को समय से पहले यह जानना होगा कि उसे कितनी जगह की आवश्यकता होगी। यह हमेशा संभव नहीं है, क्योंकि क्या होगा यदि yएक तर्क कोड के किसी अन्य टुकड़े से अलग से संकलित किया जाता है?


26

जावा वास्तव में ऐसा करने के लिए "BigInteger" और "BigDecimal" नामक कक्षाओं का उपयोग करता है, जैसा कि C ++ का GMP C ++ क्लास इंटरफ़ेस जाहिरा तौर पर करता है (धन्यवाद डिजिटल ट्रॉमा)। यदि आप चाहें तो आप इसे आसानी से किसी भी भाषा में स्वयं कर सकते हैं।

सीपीयू में हमेशा बीसीडी (बाइनरी कोडेड डेसीमल) का उपयोग करने की क्षमता होती है, जिसे किसी भी लंबाई के संचालन का समर्थन करने के लिए डिज़ाइन किया गया है (लेकिन आप एक बार में एक बाइट पर मैन्युअल रूप से काम करते हैं जो कि आज के GPU मानकों से कम होगा।)

कारण हम इन या अन्य समान समाधानों का उपयोग नहीं करते हैं? प्रदर्शन। आपकी सबसे उच्च प्रदर्शन वाली भाषाएँ कुछ तंग पाश ऑपरेशन के बीच में एक चर का विस्तार करने का जोखिम नहीं उठा सकती हैं - यह बहुत गैर-नियतात्मक होगा।

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

एक बार जब यह आपके कंप्यूटर पर होता है, जहां इसका उपयोग किया जा सकता है, तो मेमोरी सस्ती होती है, लेकिन रिज़र्व करने योग्य चर की गति और जटिलता नहीं होती है .. यह वास्तव में एकमात्र कारण है।


4
किसी को BigInteger का उल्लेख करते हुए खुशी होती है। ऐसा नहीं है कि यह एक मूर्खतापूर्ण विचार है, यह सिर्फ इतना है कि यह केवल बहुत बड़ी संख्या के लिए इसे करने के लिए समझ में आता है।
मैक्स बैराक्लो

1
पांडित्य होने का मतलब है कि आप वास्तव में बेहद सटीक संख्या हैं :) खैर कम से कम बिगडेसिमल के मामले में ...
बिल के

2
और चूंकि यह सी ++ टैग किया गया है , इसलिए शायद यह जीएमपी सी ++ क्लास इंटरफ़ेस के लायक है , जो कि जावा के बिग * के समान विचार है।
डिजिटल ट्रॉमा

20

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

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

बेहतर तरीके से समझते हैं कि कंप्यूटर कैसे काम करता है और क्यों चर में लगातार आकार होते हैं, कोडांतरक भाषा की मूल बातें जानें।

हालांकि, मुझे लगता है कि कॉन्स्टैक्स मूल्यों के साथ ऐसा कुछ हासिल करना संभव होगा। हालांकि, यह प्रोग्रामर के लिए कोड को कम अनुमानित कर देगा। मुझे लगता है कि कुछ संकलक अनुकूलन कुछ ऐसा कर सकते हैं लेकिन वे चीजों को सरल रखने के लिए इसे प्रोग्रामर से छिपाते हैं।

मैंने यहां केवल उन समस्याओं का वर्णन किया है जो एक कार्यक्रम के प्रदर्शन की चिंता करते हैं। मैंने उन सभी समस्याओं को छोड़ दिया जिन्हें चर के आकार को कम करके स्मृति को बचाने के लिए हल करना होगा। ईमानदारी से, मुझे नहीं लगता कि यह संभव भी है।


अंत में, घोषित की तुलना में छोटे चर का उपयोग करना केवल तभी समझ में आता है जब उनके मूल्यों को संकलन के दौरान जाना जाता है। यह काफी संभावना है कि आधुनिक संकलक ऐसा करते हैं। अन्य मामलों में यह बहुत अधिक कठिन या असाध्य समस्याओं का कारण होगा।


मुझे अत्यधिक संदेह है कि ऐसा काम संकलन-समय के दौरान किया जाता है। इस तरह संकलक मेमोरी को संरक्षित करने में बहुत कम बिंदु है, और यह एकमात्र लाभ है।
बार्टेक बैंचेविक्ज़

1
मैं संचालन के बारे में सोच रहा था जैसे कि सामान्य चर द्वारा कॉन्स्ट्रेक्स चर को गुणा करना। उदाहरण के लिए हमारे पास (सैद्धांतिक रूप से) 8-बाइट कॉन्टेक्स्ट वेरिएबल वैल्यू के साथ है 56और हम इसे कुछ 2-बाइट वेरिएबल से गुणा कर रहे हैं। कुछ आर्किटेक्चर पर 64-बिट ऑपरेशन अधिक गणना भारी होगा इसलिए कंपाइलर केवल 16-बिट गुणा करने के लिए अनुकूलन कर सकता है।
NO_NAME

कुछ APL कार्यान्वयन और SNOBOL परिवार में कुछ भाषाएँ (SPITBOL मुझे लगता है? हो सकता है कि आइकॉन) ने ठीक यही किया है (बारीकता के साथ): प्रतिनिधित्व मूल्यों को गतिशील रूप से वास्तविक मूल्यों के आधार पर बदल दें। एपीएल बूलियन से पूर्णांक तक तैरने और वापस जाने के लिए जाएगा। SPITBOL बूलियन्स (बाइट सरणी में संग्रहीत 8 अलग बूलियन सरणियों) के पूर्णांक (IIRC) के कॉलम प्रतिनिधित्व से जाएगा।
द्विविदक

16

फिर myIntमेरे संकलक के साथ 4 बाइट पर कब्जा होगा। हालांकि, वास्तविक मूल्य, 255केवल 1 बाइट के साथ दर्शाया जा सकता है, इसलिए myIntस्मृति के 1 बाइट पर कब्जा क्यों नहीं किया जाएगा ?

इसे चर-लंबाई एन्कोडिंग के रूप में जाना जाता है , विभिन्न एनकोडिंग परिभाषित हैं, उदाहरण के लिए वीएलक्यू । सबसे प्रसिद्ध में से एक, हालांकि, संभवतया UTF-8 है : UTF-8 1 से 4 तक, बाइट्स की एक चर संख्या पर कोड बिंदुओं को कूटबद्ध करता है।

या पूछने का अधिक सामान्यीकृत तरीका: किसी प्रकार का केवल एक ही आकार क्यों होता है जब मूल्य को दर्शाने के लिए आवश्यक स्थान उस आकार से छोटा हो सकता है?

हमेशा की तरह इंजीनियरिंग में, यह सब ट्रेड-ऑफ के बारे में है। ऐसा कोई समाधान नहीं है जिसके केवल फायदे हैं, इसलिए आपको अपने समाधान को डिजाइन करते समय फायदे और व्यापार-संतुलन को संतुलित करना होगा।

जिस डिजाइन पर समझौता किया गया था, वह निश्चित आकार के मूलभूत प्रकारों का उपयोग करने के लिए था, और हार्डवेयर / भाषाएं वहां से नीचे उड़ गईं।

तो, चर एन्कोडिंग की मूलभूत कमजोरी क्या है , जिसके कारण इसे अधिक मेमोरी वाली भूख योजनाओं के पक्ष में खारिज कर दिया गया? रैंडम एड्रेसिंग नहीं

बाइट का सूचकांक क्या है जिस पर UTF-8 स्ट्रिंग में 4 वाँ कोड बिंदु शुरू होता है?

यह पिछले कोड बिंदुओं के मूल्यों पर निर्भर करता है, एक रैखिक स्कैन की आवश्यकता होती है।

निश्चित रूप से चर-लंबाई एन्कोडिंग योजनाएं हैं जो यादृच्छिक-पते पर बेहतर हैं?

हां, लेकिन वे अधिक जटिल भी हैं। यदि कोई आदर्श है, तो मैंने इसे अभी तक नहीं देखा है।

क्या रैंडम एड्रेसिंग वास्तव में वैसे भी मायने रखता है?

अरे हाँ!

बात यह है कि, किसी भी प्रकार का समुच्चय निश्चित आकार के प्रकारों पर निर्भर करता है:

  • के 3 क्षेत्र तक पहुँचने struct? रैंडम एड्रेसिंग!
  • किसी सरणी के 3 तत्व तक पहुँचना? रैंडम एड्रेसिंग!

जिसका अर्थ है कि आपके पास अनिवार्य रूप से निम्नलिखित ट्रेड-ऑफ है:

निश्चित आकार प्रकार या रैखिक मेमोरी स्कैन


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

2
@Artelius: जब पूर्णांक की चर चौड़ाई होती है तो आप वेक्टर टेबल को कैसे एनकोड करते हैं? इसके अलावा, सदिश तालिका का मेमोरी ओवरहेड क्या है जब एक पूर्णांक के लिए एन्कोडिंग है जो मेमोरी में 1 से 4 बाइट्स का उपयोग करता है?
मैथ्यू एम।

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

@ आर्टिलियस: ध्यान दें कि पायथन में सरणी में तत्वों के लिए निश्चित आकार के संकेत होते हैं; यह एक तत्व को एक अप्रत्यक्ष की कीमत पर, ओ (1) बनाता है।
मैथ्यू एम।

16

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

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


"X, Y के जीवनकाल के भीतर N बाइट्स की तुलना में बड़ा नहीं हो पाएगा, जब तक कि X या Y स्थानांतरित नहीं हो जाता। X को स्थानांतरित करने के लिए, यह आवश्यक होगा कि X के पते को धारण करने वाले ब्रह्मांड में सब कुछ प्रतिबिंबित करने के लिए अपडेट किया जाए। नया है, और इसी तरह वाई को स्थानांतरित करने के लिए। " बहुत आकार के रूप में है कि केवल उपयोग की वस्तुओं के रूप में उनके वर्तमान मूल्य जरूरतों से जोड़ना होगा: यह IMO मुख्य बिंदु है टन यह जब एक विचार करता है कि कैसे कभी काम कर सकता था आकार / प्रहरी, स्मृति चलती, संदर्भ रेखांकन, आदि और काफी स्पष्ट के लिए भूमि के ऊपर का ... लेकिन फिर भी, बहुत स्पष्ट रूप से बताते हुए, विशेष रूप से कुछ अन्य लोगों ने किया।
अंडरस्कोर_ड

@underscore_d: जावास्क्रिप्ट जैसी भाषाएं जो चर-आकार की वस्तुओं से निपटने के लिए जमीन से डिज़ाइन की जाती हैं, वे इसके लिए आश्चर्यजनक रूप से कुशल हो सकते हैं। दूसरी ओर, जबकि परिवर्तनशील आकार के ऑब्जेक्ट सिस्टम को सरल बनाना संभव है, और उन्हें तेजी से बनाना संभव है, सरल कार्यान्वयन धीमी गति से होते हैं और तेजी से कार्यान्वयन बेहद जटिल होते हैं।
सुपरकैट

13

संक्षिप्त उत्तर है: क्योंकि C ++ मानक ऐसा कहता है।

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

एक अन्य बिंदु पर विचार करना है कि कंप्यूटर मेमोरी कैसे काम करती है। मान लीजिए कि आपका पूर्णांक प्रकार 1 से 4 बाइट्स के संग्रहण के बीच कहीं भी हो सकता है। मान लीजिए कि आप अपने पूर्णांक में मान 42 स्टोर करते हैं: यह 1 बाइट लेता है, और आप इसे मेमोरी एड्रेस एक्स पर रखते हैं। फिर आप अपने अगले वेरिएबल को स्थान X + 1 पर स्टोर करते हैं (मैं इस बिंदु पर संरेखण पर विचार नहीं कर रहा हूं) और इसी तरह । बाद में आप अपने मूल्य को 6424 में बदलने का निर्णय लेते हैं।

लेकिन यह एक भी बाइट में फिट नहीं है! तो तुम क्या करते हो? बाकियों को कहां रखा जाए? आपके पास पहले से ही X + 1 पर कुछ है, इसलिए इसे वहां नहीं रख सकते। कहीं और? बाद में कैसे पता चलेगा कि कहां कंप्यूटर मेमोरी सम्मिलित शब्दार्थों का समर्थन नहीं करती है: आप किसी स्थान पर बस कुछ नहीं रख सकते हैं और कमरा बनाने के लिए सब कुछ एक तरफ धकेल देंगे!

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


11

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

यह हमेशा नहीं होता है कि यह कैसे किया जाता है। Google के प्रोटोबॉफ़ प्रोटोकॉल पर विचार करें। Protobufs को बहुत कुशलता से डेटा संचारित करने के लिए डिज़ाइन किया गया है। प्रेषित बाइट्स की संख्या में कमी डेटा पर काम करते समय अतिरिक्त निर्देशों की लागत के लायक है। तदनुसार, प्रोटोबुफ़ एक एन्कोडिंग का उपयोग करते हैं जो 1, 2, 3, 4 या 5 बाइट्स में पूर्णांकों को एनकोड करता है, और छोटे पूर्णांक कम बाइट्स लेते हैं। एक बार जब संदेश प्राप्त होता है, हालांकि, इसे अधिक पारंपरिक निश्चित-आकार के पूर्णांक प्रारूप में अनपैक किया जाता है, जिस पर काम करना आसान होता है। यह केवल नेटवर्क ट्रांसमिशन के दौरान है कि वे इस तरह के एक अंतरिक्ष-कुशल चर लंबाई पूर्णांक का उपयोग करते हैं।


11

मुझे सर्गेई का घर सादृश्य पसंद है , लेकिन मुझे लगता है कि एक कार सादृश्य बेहतर होगा।

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

यदि आपके पास एक लिमोसिन है और आप अकेले ड्राइव कर रहे हैं, तो यह केवल आपको फिट करने के लिए सिकुड़ने वाला नहीं है। ऐसा करने के लिए, आपको कार को बेचना होगा (पढ़ें: डीललोकेट) और अपने लिए एक नया छोटा खरीदें।

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

दूसरे शब्दों में, यह निर्धारित करने की कोशिश करना कि आपको रन टाइम में कितनी मेमोरी पढ़ने की जरूरत है, इस तथ्य को बेहद अक्षम और तथ्य से बाहर कर देगा कि आप शायद अपनी पार्किंग में कुछ और कारों को फिट कर सकते हैं।


10

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

एक दूसरा यह है कि सरल प्रकारों को संग्रहीत करने का मतलब है कि उन्हें लंबाई रखने के लिए एक अतिरिक्त बाइट की आवश्यकता है। तो, 255 या उससे कम के मूल्य को वास्तव में इस नई प्रणाली में दो बाइट्स की आवश्यकता होती है, एक नहीं, और सबसे खराब स्थिति में अब आपको 4. के बजाय 5 बाइट्स की आवश्यकता है। इसका मतलब है कि उपयोग की गई मेमोरी के मामले में प्रदर्शन जीत आपकी तुलना में कम है सोचो और कुछ बढ़त के मामलों में वास्तव में शुद्ध नुकसान हो सकता है।

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

फुटनोट: जब आप बाइट्स में संबोधित करते हैं तो अधिक सटीक होने के लिए, अधिकांश सिस्टम ने 'असमान' बाइट्स को नजरअंदाज कर दिया। Ie, पता 0, 1, 2 और 3 सभी एक ही शब्द पढ़ते हैं, 4, 5, 6 और 7 अगला शब्द पढ़ते हैं, और इसी तरह।

एक असंबंधित नोट पर, यही कारण है कि 32-बिट सिस्टम में अधिकतम 4 जीबी मेमोरी थी। स्मृति में स्थानों को संबोधित करने के लिए उपयोग किए जाने वाले रजिस्टर आमतौर पर एक शब्द धारण करने के लिए पर्याप्त रूप से बड़े होते हैं, अर्थात 4 बाइट्स, जिसका अधिकतम मूल्य (2 ^ 32) -1 = 4294967295 है। 4294967296 बाइट्स 4 जीबी है।


8

ऐसी वस्तुएँ हैं जो कुछ अर्थों में, C ++ मानक पुस्तकालय में चर आकार में हैं, जैसे कि std::vector। हालांकि, ये सभी गतिशील रूप से अतिरिक्त मेमोरी आवंटित करते हैं जिनकी उन्हें आवश्यकता होगी। यदि आप लेते हैं sizeof(std::vector<int>), तो आपको एक स्थिरांक मिलेगा जिसका ऑब्जेक्ट द्वारा प्रबंधित मेमोरी के साथ कोई लेना-देना नहीं है, और यदि आप किसी सरणी या संरचना को आवंटित करते हैं std::vector<int>, तो यह उसी सरणी या संरचना में अतिरिक्त भंडारण रखने के बजाय इस आधार आकार को आरक्षित करेगा। । सी सिंटैक्स के कुछ टुकड़े हैं जो इस तरह से कुछ का समर्थन करते हैं, विशेष रूप से चर-लंबाई सरणियों और संरचनाएं, लेकिन सी ++ ने उन्हें समर्थन करने के लिए नहीं चुना।

भाषा मानक वस्तु के आकार को इस तरह परिभाषित करता है ताकि संकलक कुशल कोड उत्पन्न कर सके। उदाहरण के लिए, यदि intकुछ कार्यान्वयन पर 4 बाइट्स लंबा होता है, और आप aएक पॉइंटर के रूप में या intमूल्यों की सरणी के लिए घोषित करते हैं, तो a[i]छद्मकोड में अनुवाद करता है, "पते को + 4 × i।" यह निरंतर समय में किया जा सकता है, और एक ऐसा सामान्य और महत्वपूर्ण ऑपरेशन है कि कई निर्देश-सेट आर्किटेक्चर, जिसमें x86 और DEC PDP मशीनें शामिल हैं, जिन पर C मूल रूप से विकसित किया गया था, एक मशीन निर्देश में कर सकते हैं।

चर-लंबाई इकाइयों के रूप में लगातार संग्रहीत डेटा का एक सामान्य वास्तविक दुनिया उदाहरण है, जो UTF-8 के रूप में एन्कोडेड है। (हालांकि, संकलित करने के लिए UTF-8 स्ट्रिंग का अंतर्निहित प्रकार अभी भी है charऔर इसकी चौड़ाई 1 है। यह ASCII स्ट्रिंग्स को मान्य UTF-8 के रूप में व्याख्या करने की अनुमति देता है, और बहुत सारे पुस्तकालय कोड जैसे कि strlen()और strncpy()काम जारी रखने के लिए।) किसी भी UTF-8 कोडपॉइंट की एन्कोडिंग एक से चार बाइट्स लंबी हो सकती है, और इसलिए, यदि आप एक स्ट्रिंग में पाँचवाँ UTF-8 कोडपॉइंट चाहते हैं, तो यह डेटा के पांचवें बाइट से लेकर सत्रहवें बाइट तक कहीं भी शुरू हो सकता है। इसे खोजने का एकमात्र तरीका स्ट्रिंग की शुरुआत से स्कैन करना और प्रत्येक कोडपॉइंट के आकार की जांच करना है। अगर आप पाँचवाँ अंगूर लेना चाहते हैं, आपको चरित्र वर्गों की जांच करने की भी आवश्यकता है। यदि आप एक स्ट्रिंग में मिलियन UTF-8 वर्ण ढूंढना चाहते हैं, तो आपको इस लूप को एक लाख बार चलाने की आवश्यकता होगी! यदि आप जानते हैं कि आपको अक्सर सूचकांकों के साथ काम करने की आवश्यकता होगी, तो आप एक बार स्ट्रिंग को आगे बढ़ा सकते हैं और इसका एक सूचकांक बना सकते हैं- या आप एक निश्चित-चौड़ाई वाले एन्कोडिंग में बदल सकते हैं, जैसे कि यूसीएस -4। एक स्ट्रिंग में दसवें UCS-4 वर्ण को ढूंढना सरणी के पते पर चार मिलियन जोड़ने की बात है।

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

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

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


7

किसी प्रकार का केवल एक ही आकार इससे क्यों जुड़ा होता है जब मान को दर्शाने के लिए आवश्यक स्थान उस आकार से छोटा हो सकता है?

मुख्य रूप से संरेखण आवश्यकताओं के कारण।

Basic.align / 1 के अनुसार :

ऑब्जेक्ट प्रकारों में संरेखण आवश्यकताएँ होती हैं जो उन पतों पर प्रतिबंध लगाती हैं जिन पर उस प्रकार की कोई वस्तु आवंटित की जा सकती है।

एक ऐसी इमारत के बारे में सोचें जिसमें कई मंजिलें हों और हर मंजिल में कई कमरे हों।
प्रत्येक कमरा आपका आकार (एक निश्चित स्थान) है जो लोगों या वस्तुओं की एन राशि रखने में सक्षम है।
पहले से ज्ञात कमरे के आकार के साथ, यह इमारत के संरचनात्मक घटक को अच्छी तरह से संरचित करता है

यदि कमरे संरेखित नहीं हैं, तो भवन कंकाल अच्छी तरह से संरचित नहीं होगा।


7

यह कम हो सकता है। फ़ंक्शन पर विचार करें:

int foo()
{
    int bar = 1;
    int baz = 42;
    return bar+baz;
}

यह असेंबली कोड (g ++, x64, विवरण छीन लिया गया) के लिए संकलित है

$43, %eax
ret

यहां, barऔर bazअंत में प्रतिनिधित्व करने के लिए शून्य बाइट्स का उपयोग करें।


5

तो क्यों myInt सिर्फ 1 बाइट मेमोरी पर कब्जा नहीं करेगा?

क्योंकि आपने इसे इतना ही इस्तेमाल करने के लिए कहा था। ए का उपयोग करते समय unsigned int, कुछ मानक तय करते हैं कि 4 बाइट का उपयोग किया जाएगा और इसके लिए उपलब्ध रेंज 0 से 4,294,967,295 तक होगी। यदि आप unsigned charइसके बजाय उपयोग करना चाहते थे , तो आप शायद केवल 1 बाइट का उपयोग कर रहे होंगे जो आप देख रहे हैं, (मानक के आधार पर और C ++ सामान्य रूप से इन मानकों का उपयोग करता है)।

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

इस कारण से कि हम प्रति बाइट 8 बिट्स का उपयोग करते हैं, आप इस पर एक नज़र डाल सकते हैं: बाइट्स आठ बिट्स का इतिहास क्या है?

एक साइड नोट पर, आप पूर्णांक को ओवरफ्लो करने की अनुमति दे सकते हैं; लेकिन क्या आपको एक हस्ताक्षरित पूर्णांक, C \ C ++ मानकों का उपयोग करना चाहिए जो पूर्णांक को अपरिभाषित व्यवहार में परिणत करता है। पूर्णांक अतिप्रवाह


5

कुछ सरल जो अधिकतर उत्तर याद आते हैं:

क्योंकि यह C ++ के डिज़ाइन लक्ष्यों के अनुरूप है।

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

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

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


5

एक चर के आकार को बदलने के लिए वास्तविककरण की आवश्यकता होगी और यह आमतौर पर मेमोरी के कुछ और बाइट्स को बर्बाद करने की तुलना में अतिरिक्त सीपीयू चक्रों के लायक नहीं है।

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

एक और तरीका है कि आप इसे कर सकते हैं हर चर को एक ढेर स्थान पर एक संकेतक बनाकर, लेकिन आप इस तरह से और भी अधिक सीपीयू साइकिल और मेमोरी को बर्बाद कर देंगे, वास्तव में। संकेत 4 बाइट्स (32 बिट एड्रेसिंग) या 8 बाइट्स (64 बिट एड्रेसिंग) हैं, इसलिए आप पहले से ही पॉइंटर के लिए 4 या 8 का उपयोग कर रहे हैं, फिर ढेर पर डेटा का वास्तविक आकार। इस मामले में अभी भी वसूली की लागत है। यदि आपको ढेर डेटा को पुनः प्राप्त करने की आवश्यकता है, तो आप भाग्यशाली हो सकते हैं और इसमें इनलाइन का विस्तार करने के लिए जगह हो सकती है, लेकिन कभी-कभी आपको इसे अपने इच्छित आकार की स्मृति के सन्निहित ब्लॉक के लिए कहीं और स्थानांतरित करना होगा।

यह तय करना हमेशा तेज होता है कि पहले से कितनी मेमोरी का उपयोग करना है। यदि आप गतिशील आकार देने से बच सकते हैं तो आप प्रदर्शन हासिल कर सकते हैं। मेमोरी बर्बाद करना आमतौर पर प्रदर्शन लाभ के लायक है। इसलिए कंप्यूटर में टन मेमोरी होती है। :)


3

कंपाइलर को आपके कोड में बहुत से बदलाव करने की अनुमति है, जब तक कि चीजें अभी भी काम करती हैं ("as-is" नियम)।

पूर्ण स्थानांतरित करने के लिए आवश्यक लंबे (32/64 बिट) के बजाय 8-बिट शाब्दिक चाल अनुदेश का उपयोग करना संभव होगा int। हालाँकि, आपको लोड पूरा करने के लिए दो निर्देशों की आवश्यकता होगी, क्योंकि आपको लोड करने से पहले रजिस्टर को शून्य पर सेट करना होगा।

यह 32 बिट के रूप में मान को संभालने के लिए बस अधिक कुशल (मुख्य संकलक के अनुसार कम से कम) है। वास्तव में, मुझे अभी तक एक x86 / x86_64 संकलक देखना है जो इनलाइन असेंबली के बिना 8-बिट लोड करेगा।

हालांकि, 64 बिट की बात आने पर चीजें अलग होती हैं। अपने प्रोसेसर के पिछले एक्सटेंशन (16 से 32 बिट तक) डिजाइन करते समय, इंटेल ने एक गलती की। यहाँ एक अच्छा प्रतिनिधित्व है कि वे क्या दिखते हैं। यहाँ मुख्य टेकअवे यह है कि जब आप AL या AH को लिखते हैं, तो दूसरा प्रभावित नहीं होता है (पर्याप्त रूप से, यह बिंदु था और यह तब समझ में आया)। लेकिन यह दिलचस्प हो जाता है जब उन्होंने इसे 32 बिट्स तक विस्तारित किया। आप नीचे बिट्स (AL, एएच या कुल्हाड़ी) लिखते हैं, कुछ भी नहीं EAX के ऊपरी 16 बिट, जिसका मतलब है कि अगर आप एक को बढ़ावा देना चाहते करने के लिए होता charएक में int, आपको पहले कि स्मृति साफ करने की आवश्यकता है, लेकिन आप का कोई तरीका नहीं वास्तव में केवल इन शीर्ष 16 बिट्स का उपयोग करते हुए, इस "सुविधा" को किसी भी चीज़ से अधिक दर्द होता है।

अब 64 बिट्स के साथ, एएमडी ने बहुत बेहतर काम किया। यदि आप निचले 32 बिट्स में कुछ भी छूते हैं, तो ऊपरी 32 बिट्स बस 0. पर सेट हो जाते हैं। इससे कुछ वास्तविक अनुकूलन हो जाते हैं जिन्हें आप इस गॉडबॉल में देख सकते हैं । आप देख सकते हैं कि 8 बिट्स या 32 बिट्स में से कुछ को लोड करना उसी तरह से किया जाता है, लेकिन जब आप 64 बिट्स चर का उपयोग करते हैं, तो कंपाइलर आपके शाब्दिक के वास्तविक आकार के आधार पर एक अलग निर्देश का उपयोग करता है।

तो आप यहां देख सकते हैं, कंपाइलर सीपीयू के अंदर आपके वैरिएबल के वास्तविक आकार को पूरी तरह से बदल सकते हैं यदि यह समान परिणाम देगा, लेकिन यह छोटे प्रकार के लिए ऐसा करने का कोई मतलब नहीं है।


सुधार: के रूप में अगर । इसके अलावा, मैं यह नहीं देखता कि कैसे, यदि एक छोटे लोड / स्टोर का उपयोग किया जा सकता है, जो उपयोग के लिए अन्य बाइट्स को मुक्त कर देगा - जो ऐसा प्रतीत होता है कि ओपी आश्चर्यचकित करता है: न केवल वर्तमान मूल्य द्वारा आवश्यक स्मृति को छूने से परहेज करना, लेकिन यह बताने में सक्षम होने के लिए कि कितने बाइट्स पढ़ने हैं, और जादुई रूप से सभी रैम को रनटाइम के आसपास स्थानांतरित करना है, इसलिए अंतरिक्ष-दक्षता के कुछ अजीब दार्शनिक विचार (कभी भी विशाल प्रदर्शन लागत का मन नहीं करता है!) से मुलाकात की है ... बस निचले-पायदान के निर्देशों को जीता है। 't' को हल करें। सीपीयू / ओएस को ऐसा करने की आवश्यकता होगी जो इतना जटिल होगा कि यह सवाल का स्पष्ट रूप से आईएमओ को जवाब दे।
अंडरस्कोर_ड

1
आप वास्तव में रजिस्टरों में "मेमोरी सेव" नहीं कर सकते। जब तक आप एएच और एएल को गाली देकर कुछ अजीब करने की कोशिश नहीं कर रहे हैं, तब तक आप एक ही सामान्य उद्देश्य रजिस्टर में कई अलग-अलग मूल्य नहीं रख सकते। स्थानीय चर अक्सर रजिस्टरों में रहते हैं और अगर इसकी कोई आवश्यकता नहीं है तो कभी भी रैम पर न जाएं।
23
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.