c - किसी सरणी का अधिकतम आकार "बहुत बड़ा" क्यों है?




gcc mingw (3)

SIZE_MAX / 2 की सीमा आपके कार्यान्वयन पर size_t और ptrdiff_t की परिभाषा से आती है, जो चुनते हैं कि ptrdiff_t और size_t की चौड़ाई समान है।

C मानक 1 को टाइप करता है जो size_t अहस्ताक्षरित है और टाइप ptrdiff_t हस्ताक्षरित है।

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

इस प्रकार मान SIZE_MAX / 2 मान PTRDIFF_MAX के बराबर होता है। यदि कार्यान्वयन अधिकतम ऑब्जेक्ट आकार SIZE_MAX होना चाहता है, तो ptrdiff_t प्रकार की चौड़ाई बढ़ानी होगी। लेकिन SIZE_MAX / 2 के लिए ऑब्जेक्ट के अधिकतम आकार को सीमित करना बहुत आसान है, तो यह है कि टाइप करने के लिए ptrdiff_t के पास size_t की तुलना में अधिक या समान सकारात्मक सीमा है।

मानक विषय पर इन 3 टिप्पणियों 4 प्रदान करता है।

(आईएसओ / आईईसी 9899: 201x से उद्धृत)

1 (7.19 सामान्य परिभाषा 2)
प्रकार हैं
ptrdiff_t
जो दो बिंदुओं को घटाने के परिणाम पर हस्ताक्षरित पूर्णांक प्रकार है;
size_t
जो आकार के ऑपरेटर के परिणाम का अहस्ताक्षरित पूर्णांक प्रकार है;

2 (6.5.6 एडिटिव ऑपरेटर 9)
जब दो बिंदुओं को घटाया जाता है, तो दोनों एक ही एरे ऑब्जेक्ट के तत्वों को इंगित करेंगे, या एरे ऑब्जेक्ट के अंतिम तत्व को एक अतीत; परिणाम दो सरणी तत्वों की सदस्यता का अंतर है। परिणाम का आकार कार्यान्वयन-परिभाषित है, और इसका प्रकार (एक हस्ताक्षरित पूर्णांक प्रकार) हेडर में परिभाषित ptrdiff_t है। यदि परिणाम उस प्रकार के ऑब्जेक्ट में प्रतिनिधित्व करने योग्य नहीं है, तो व्यवहार अपरिभाषित है।

3 (K.3.4 पूर्णांक प्रकार 3)
अत्यधिक बड़े ऑब्जेक्ट आकार अक्सर एक संकेत होते हैं कि किसी ऑब्जेक्ट का आकार गलत तरीके से गणना किया गया था। उदाहरण के लिए, ऋणात्मक संख्याएँ बहुत बड़ी धनात्मक संख्याओं के रूप में दिखाई देती हैं जब size_t जैसे अहस्ताक्षरित प्रकार में परिवर्तित होती हैं। इसके अलावा, कुछ कार्यान्वयन वस्तुओं का समर्थन नहीं करते हैं जितना कि अधिकतम मूल्य जितना कि type size_t द्वारा दर्शाया जा सकता है।

4 (K.3.4 पूर्णांक प्रकार 4)
उन कारणों से, प्रोग्रामिंग त्रुटियों का पता लगाने के लिए कभी-कभी ऑब्जेक्ट आकारों की सीमा को सीमित करना फायदेमंद होता है। बड़े पते वाले स्थानों के साथ मशीनों को लक्षित करने वाले कार्यान्वयन के लिए, यह अनुशंसा की जाती है कि RSIZE_MAX को सबसे बड़ी वस्तु के आकार के छोटे के रूप में परिभाषित किया जाए जो समर्थित (या SIZE_MAX >> 1) है, भले ही यह सीमा कुछ वैध के आकार से छोटी हो, लेकिन बहुत बड़ी, वस्तुएं। छोटे पते के स्थानों के साथ मशीनों को लक्षित करने वाले कार्यान्वयन RSIZE_MAX को SIZE_MAX के रूप में परिभाषित करना चाह सकते हैं, जिसका अर्थ है कि कोई ऑब्जेक्ट आकार नहीं है जिसे रनटाइम-बाधा उल्लंघन माना जाता है।

मैं इस उत्तर के समान ही प्रभावित हूं, कि size_t को हमेशा मानक द्वारा गारंटी दी जाती है कि किसी दिए गए सिस्टम के सबसे बड़े संभावित प्रकार को धारण करने के लिए पर्याप्त बड़ा हो।

हालाँकि, यह कोड gcc / मिंगव पर संकलित करने में विफल है:

#include <stdint.h>
#include <stddef.h>

typedef uint8_t array_t [SIZE_MAX];

त्रुटि: सरणी 'array_t' का आकार बहुत बड़ा है

क्या मैं यहाँ के मानक में कुछ गलत समझ रहा हूँ? क्या किसी दिए गए कार्यान्वयन के लिए size_t को बहुत बड़ा होने की अनुमति है? या मिंगव में यह एक और बग है?

संपादित करें: आगे के शोध से पता चलता है कि

typedef uint8_t array_t [SIZE_MAX/2];   // does compile
typedef uint8_t array_t [SIZE_MAX/2+1]; // does not compile

जो जैसा होता है वैसा ही होता है

#include <limits.h>

typedef uint8_t array_t [LLONG_MAX];           // does compile
typedef uint8_t array_t [LLONG_MAX+(size_t)1]; // does not compile

इसलिए मैं अब यह मानना ​​चाहता हूं कि यह मिंगव में एक बग है, क्योंकि हस्ताक्षरित पूर्णांक प्रकार के आधार पर अधिकतम अनुमत आकार सेट करने का कोई मतलब नहीं है।


खरोंच से तर्क करना, size_t एक प्रकार है जो किसी भी वस्तु के आकार को पकड़ सकता है। किसी भी वस्तु का आकार पता बस की चौड़ाई (मल्टीप्लेक्सिंग और सिस्टम जो 32 और 64 बिट कोड को संभाल सकता है, को अनदेखा करके सीमित है), जिसे "कोड चौड़ाई" कहा जाता है। MAX_INT के लिए सबसे बड़ा पूर्णांक मान, SIZE_MAX , size_t का सबसे बड़ा मान है। इस प्रकार, आकार SIZE_MAX का एक उद्देश्य सभी पता योग्य मेमोरी है। यह उचित है कि एक कार्यान्वयन झंडे जो एक त्रुटि के रूप में, हालांकि, मैं मानता हूं कि यह केवल उस मामले में त्रुटि है जहां एक वास्तविक वस्तु आवंटित की जाती है, यह स्टैक पर या वैश्विक मेमोरी में हो। (उस राशि के लिए malloc लिए एक कॉल वैसे भी विफल हो जाएगी)


रेंज size_t को कार्यान्वयन द्वारा समर्थित सबसे बड़ी वस्तु के आकार को संग्रहीत करने के लिए पर्याप्त होने की गारंटी है। रिवर्स सच नहीं है: आपको ऐसी कोई वस्तु बनाने में सक्षम होने की गारंटी नहीं है जिसका आकार size_t की पूरी श्रृंखला को भरता है।

ऐसी परिस्थितियों में सवाल यह है: SIZE_MAX किस लिए खड़ा है? सबसे बड़ा समर्थित ऑब्जेक्ट आकार? या size_t में सबसे बड़ा size_t ? उत्तर है: यह बाद वाला है, अर्थात SIZE_MAX (size_t) -1 । आपको यह सुनिश्चित करने की गारंटी नहीं है कि ऑब्जेक्ट SIZE_MAX बाइट्स बड़ी बनाने में सक्षम हैं।

इसके पीछे कारण यह है कि size_t अलावा, कार्यान्वयन को ptrdiff_t भी प्रदान करना चाहिए, जिसका उद्देश्य (लेकिन गारंटी नहीं है) एक ही सरणी ऑब्जेक्ट में इंगित करने वाले दो ptrdiff_t बीच अंतर को संग्रहीत करने के लिए। चूंकि ptrdiff_t पर हस्ताक्षर किए गए हैं, इसलिए कार्यान्वयन निम्नलिखित विकल्पों के साथ सामना कर रहे हैं:

  1. आकार की वस्तुओं को SIZE_MAX की अनुमति दें और आकार से ptrdiff_t व्यापक ptrdiff_t । इसे कम से कम एक बिट से व्यापक करना होगा। ऐसे ptrdiff_t दो ptrdiff_t बीच किसी भी अंतर को आकार SIZE_MAX या छोटे आकार में SIZE_MAX कर सकते हैं।

  2. आकार SIZE_MAX की सरणी ऑब्जेक्ट को अनुमति दें और आकार के समान चौड़ाई का ptrdiff_t उपयोग करें। इस तथ्य को स्वीकार करें कि सूचक घटाव अतिप्रवाह कर सकता है और अपरिभाषित व्यवहार का कारण बन सकता है, यदि संकेत SIZE_MAX / 2 तत्वों से अलग हैं। भाषा विनिर्देश इस दृष्टिकोण को प्रतिबंधित नहीं करता है।

  3. ptrdiff_t के समान चौड़ाई के ptrdiff_t का उपयोग करें और SIZE_MAX / 2 द्वारा अधिकतम सरणी ऑब्जेक्ट आकार को प्रतिबंधित करें। ऐसे ptrdiff_t दो ptrdiff_t बीच किसी भी अंतर को आकार SIZE_MAX / 2 या छोटे आकार में SIZE_MAX / 2 कर सकते हैं।

आप बस एक कार्यान्वयन से निपट रहे हैं जिसने तीसरे दृष्टिकोण का पालन करने का फैसला किया है।





stdint