क्या "स्ट्रक्चर हैक" तकनीकी रूप से अपरिभाषित व्यवहार है?


111

मैं जिस चीज के बारे में पूछ रहा हूं वह अच्छी तरह से जाना जाता है "किसी संरचना का अंतिम सदस्य चर लंबाई" चाल है। यह कुछ इस तरह चलता है:

struct T {
    int len;
    char s[1];
};

struct T *p = malloc(sizeof(struct T) + 100);
p->len = 100;
strcpy(p->s, "hello world");

स्मृति में जिस तरह से संरचना रखी गई है, उस वजह से हम संरचना को आवश्यक ब्लॉक से अधिक ओवरले करने में सक्षम हैं और अंतिम सदस्य के साथ ऐसा व्यवहार करते हैं जैसे कि यह 1 charनिर्दिष्ट से बड़ा हो ।

तो सवाल यह है कि क्या यह तकनीक तकनीकी रूप से अपरिभाषित व्यवहार है? । मुझे उम्मीद है कि यह है, लेकिन उत्सुक था कि मानक इस बारे में क्या कहता है।

पुनश्च: मैं इस के लिए C99 दृष्टिकोण से अवगत हूं, मैं विशेष रूप से ऊपर सूचीबद्ध के रूप में चाल के संस्करण से चिपके रहना चाहता हूं।


33
यह एक काफी स्पष्ट उचित, और सब से ऊपर की तरह लगता है जवाबदेह सवाल। करीबी वोट का कारण नहीं देख रहा है।
cHao

2
यदि आपने एक "एएनआई सी" कंपाइलर पेश किया है जो स्ट्रक्चर हैक का समर्थन नहीं करता है, तो अधिकांश सी प्रोग्रामर मुझे पता है कि आपका कंपाइलर "सही काम करता है" स्वीकार नहीं करेगा। यह समझकर नहीं कि वे मानक के सख्त पठन को स्वीकार करेंगे। समिति ने उस पर एक चूक की।
dmckee --- पूर्व-मध्यस्थ ने बिल्ली का बच्चा

4
@james एक न्यूनतम सरणी घोषित किए जाने के बावजूद, किसी ऑब्जेक्ट को बड़े पैमाने पर एंकल करके काम करता है। तो आप संरचना की सख्त परिभाषा के बाहर आवंटित स्मृति तक पहुँच रहे हैं । अपने आबंटन के अतीत को लिखना एक ग़लती है, लेकिन यह आपके आबंटन में लिखने से अलग है लेकिन "संरचना" के बाहर।
dmckee --- पूर्व-मध्यस्थ ने

2
@ नाम: ओवरसाइज़ मॉलॉक यहाँ महत्वपूर्ण है। यह सुनिश्चित करता है कि संरचना द्वारा स्मृति --- स्मृति के साथ कानूनी पता और 'स्वामित्व' है (अर्थात इसका उपयोग करने के लिए किसी अन्य इकाई के लिए अवैध है) --- संरचना के नाममात्र अंत को अतीत। ध्यान दें कि इसका मतलब है कि आप स्वचालित चर पर संरचना हैक का उपयोग नहीं कर सकते हैं: उन्हें गतिशील रूप से आवंटित किया जाना चाहिए।
dmckee --- पूर्व-मध्यस्थ ने बिल्ली का बच्चा

5
@detly: दो चीजों को आवंटित करना / डील करना आसान है / दो चीजों को आवंटित करना / निपटाना आसान है, खासकर जब से बाद वाले को असफल होने के दो तरीके हैं जिनसे आपको निपटना है। यह मेरे लिए सीमांत लागत / गति बचत से अधिक मायने रखता है।
jamesdlin

जवाबों:


52

जैसा कि C FAQ कहता है:

यह स्पष्ट नहीं है कि यह कानूनी या पोर्टेबल है, बल्कि यह लोकप्रिय है।

तथा:

... एक आधिकारिक व्याख्या ने माना है कि यह सी मानक के साथ कड़ाई से अनुरूप नहीं है, हालांकि यह सभी ज्ञात कार्यान्वयन के तहत काम करता है। (संकलक जो सरणी सीमा की सावधानीपूर्वक जाँच करते हैं चेतावनी जारी कर सकते हैं।

'कड़ाई से अनुरूप' बिट के पीछे तर्क युक्ति में है, खंड J.2 अपरिभाषित व्यवहार , जिसमें अपरिभाषित व्यवहार की सूची शामिल है:

  • एक सरणी सबस्क्रिप्ट एक सीमा से बाहर है, भले ही कोई वस्तु स्पष्ट रूप से दिए गए सबस्क्रिप्ट के साथ सुलभ हो (जैसा a[1][7]कि घोषणा में दिए गए int a[4][5]अंतराल अभिव्यक्ति में ) (6.5.6)।

धारा ६.५.६ के पैरा itive के परिशिष्ट संचालकों ने एक और उल्लेख किया है कि परिभाषित सरणी सीमा से परे पहुंच अपरिभाषित है:

यदि पॉइंटर ऑपरेटर और परिणाम दोनों एक ही एरे ऑब्जेक्ट के तत्वों को इंगित करते हैं, या एरे ऑब्जेक्ट के अंतिम तत्व के पिछले एक, मूल्यांकन एक अतिप्रवाह का उत्पादन नहीं करेगा; अन्यथा, व्यवहार अपरिभाषित है।


1
ओपी के कोड में, p->sकभी भी एक सरणी के रूप में उपयोग नहीं किया जाता है। इसे पास कर दिया जाता है strcpy, जिस स्थिति में यह एक मैदानी क्षेत्र के char *लिए तय होता है, जो किसी वस्तु को इंगित करने के लिए होता है जिसे कानूनी रूप char [100];से आवंटित वस्तु के अंदर व्याख्या की जा सकती है ।
आर .. गिटहब स्टॉप हेल्पिंग आईसीई

3
शायद इसे देखने का एक और तरीका यह है कि भाषा गर्भधारण को प्रतिबंधित कर सकती है कि आप जे .2 में वर्णित वास्तविक सरणी चर का उपयोग कैसे करें , लेकिन ऐसा कोई तरीका नहीं है जिससे आप किसी वस्तु के आवंटन के लिए इस तरह के प्रतिबंध लगा सकते हैं malloc, जब आपने केवल वापस लौटा दिया है void *एक सूचक के लिए [एक संरचना युक्त] एक सरणी। यह अभी भी char(या अधिमानतः unsigned char) एक पॉइंटर का उपयोग करके आवंटित ऑब्जेक्ट के किसी भी हिस्से तक पहुंचने के लिए वैध है ।
आर .. गिटहब स्टॉप हेल्पिंग आईसीई

@R। - मैं देख सकता हूं कि J2 इसे कैसे कवर नहीं कर सकता है, लेकिन क्या यह 6.5.6 तक भी कवर नहीं है?
detly

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

4
ऑब्जेक्ट ऐरे ऑब्जेक्ट नहीं है, इसलिए 6.5.6 अप्रासंगिक है। ऑब्जेक्ट द्वारा आवंटित मेमोरी का ब्लॉक है malloc। इससे पहले कि आप बी एस टोंटी में मानक में "ऑब्जेक्ट" देखें।
R .. गिटहब स्टॉप हेल्पिंग ICE

34

मेरा मानना ​​है कि तकनीकी रूप से यह अपरिभाषित व्यवहार है। मानक (यकीनन) इसे सीधे संबोधित नहीं करता है, इसलिए यह "या व्यवहार की किसी भी स्पष्ट परिभाषा की चूक से" होता है। खंड (C99 का ause4 / 2, C89 का 63.16 / 2) जो कहता है कि यह अपरिभाषित व्यवहार है।

ऊपर "यकीनन" सरणी सबस्क्रिप्टिंग ऑपरेटर की परिभाषा पर निर्भर करता है। विशेष रूप से, यह कहता है: "चौकोर कोष्ठक में एक अभिव्यक्ति के बाद एक उपसर्ग अभिव्यक्ति [] एक सरणी वस्तु का एक उपप्रकाशित पदनाम है।" (C89, C6.3.2.1 / 2)।

आप तर्क दे सकते हैं कि "सरणी ऑब्जेक्ट" का यहां उल्लंघन किया जा रहा है (क्योंकि आप सरणी ऑब्जेक्ट की निर्धारित सीमा के बाहर सब्सक्राइब कर रहे हैं), जिस स्थिति में व्यवहार केवल अपरिभाषित के बजाय स्पष्ट रूप से अपरिभाषित है। कुछ भी नहीं इसे शिष्टाचार शिष्टाचार।

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


2
मैं एक कंपाइलर की भी कल्पना कर सकता हूं जो यह तय कर सकता है कि अगर कोई सरणी 1 आकार का हुआ, तो arr[x] = y;उसे फिर से लिखा जा सकता है arr[0] = y;; आकार 2 की एक सरणी के लिए, arr[i] = 4;फिर से लिखा जा सकता है , i ? arr[1] = 4 : arr[0] = 4; जबकि मैंने कभी नहीं देखा है कि एक कंपाइलर इस तरह के अनुकूलन करता है, कुछ एम्बेडेड सिस्टम पर वे बहुत उत्पादक हो सकते हैं। PIC18x पर, 8-बिट डेटा प्रकारों का उपयोग करते हुए, पहले कथन का कोड सोलह बाइट्स, दूसरा, दो या चार और तीसरा, आठ या बारह होगा। कानूनी नहीं तो एक बुरा अनुकूलन।
सुपरकैट

यदि मानक सरणी के बाहर सरणी पहुंच को अपरिभाषित व्यवहार के रूप में परिभाषित करता है, तो संरचना हैक भी है। अगर, हालांकि, मानक अंकगणित अंकगणित ( a[2] == a + 2) के लिए सिंथेटिक चीनी के रूप में सरणी का उपयोग परिभाषित करता है , तो यह नहीं करता है। यदि मैं सही हूं, तो सभी C मानक सूचक को अंकगणित के रूप में एक्सेस तक परिभाषित करते हैं।
22

13

हां, यह अपरिभाषित व्यवहार है।

C भाषा दोष रिपोर्ट # 051 इस प्रश्न का निश्चित उत्तर देती है:

मुहावरे, जबकि सामान्य, कड़ाई से अनुरूप नहीं है

http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_051.html

C99 Rationale दस्तावेज़ में C समिति जोड़ता है:

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


2
इसे खोजने के लिए +1, लेकिन मैं अभी भी दावा करता हूं कि यह विरोधाभासी है। एक ही ऑब्जेक्ट के दो पॉइंटर्स (इस मामले में, दिए गए बाइट) बराबर हैं, और एक पॉइंटर इसके लिए (द्वारा प्राप्त संपूर्ण ऑब्जेक्ट के प्रतिनिधित्व सरणी में पॉइंटर malloc) इसके अलावा में मान्य है, तो समान पॉइंटर कैसे कर सकते हैं, एक और मार्ग के माध्यम से प्राप्त किया, इसके अतिरिक्त अमान्य हो सकता है? यहां तक ​​कि अगर वे दावा करना चाहते हैं कि यह यूबी है, तो यह बहुत ही निरर्थक है, क्योंकि अच्छी तरह से परिभाषित उपयोग और कथित रूप से अपरिभाषित उपयोग के बीच अंतर करने के लिए कार्यान्वयन के लिए कम्प्यूटेशनल रूप से कोई रास्ता नहीं है।
R .. GitHub STOP हेल्पिंग ICE सेप

यह बहुत बुरा है कि सी संकलक ने शून्य-लंबाई सरणियों की घोषणा को मना करना शुरू कर दिया; क्या यह उस निषेध के लिए नहीं था, कई कंपाइलरों को उन्हें "काम" के रूप में काम करने के लिए कोई विशेष हैंडलिंग नहीं करनी होती, लेकिन फिर भी वे एकल-तत्व सरणियों के लिए विशेष-केस कोड में सक्षम हो सकते हैं (उदाहरण के लिए यदि *fooइसमें एक एकल-तत्व सरणी boz, अभिव्यक्ति foo->boz[biz()*391]=9;को सरल बनाया जा सकता है biz(),foo->boz[0]=9;)। दुर्भाग्य से, कम्पाइलर्स की रिजेक्शन शून्य-तत्व सरणियों का मतलब है कि कोड का एक बहुत बजाय एकल-तत्व सरणियों का उपयोग करता है, और उस अनुकूलन से टूट जाएगा।
सुपरकैट

11

ऐसा करने का वह विशेष तरीका किसी भी C मानक में स्पष्ट रूप से परिभाषित नहीं है, लेकिन C99 में भाषा के भाग के रूप में "स्ट्रक्चर हैक" शामिल है। C99 में, किसी संरचना का अंतिम सदस्य एक "लचीला सरणी सदस्य" हो सकता है, जिसे घोषित किया जाता है char foo[](जिस प्रकार की जगह आप चाहें char)।


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

7

आधिकारिक या अन्यथा किसी की परवाह किए बिना, यह अपरिभाषित व्यवहार नहीं है , क्योंकि यह मानक द्वारा परिभाषित किया गया है। p->sको छोड़कर, जब एक लवल्यू के रूप में उपयोग किया जाता है, एक समान सूचक का मूल्यांकन करता है (char *)p + offsetof(struct T, s)। विशेष रूप से, यह charमॉलोकॉल्ड ऑब्जेक्ट के अंदर एक वैध सूचक है, और इसके तुरंत बाद 100 (या अधिक संरेखण पर निर्भरता) क्रमिक पते हैं जो charआवंटित ऑब्जेक्ट के अंदर ऑब्जेक्ट के रूप में भी मान्य हैं । तथ्य यह है कि सूचक ->द्वारा mallocडाली गई सूचक को ऑफसेट को स्पष्ट रूप से जोड़ने के बजाय का उपयोग करके व्युत्पन्न किया गया था , को डाला गया है char *, अप्रासंगिक है।

तकनीकी रूप से, संरचना के अंदर सरणी p->s[0]का एकल तत्व है char, अगले कुछ तत्व (जैसे के p->s[1]माध्यम से p->s[3]) संरचना के अंदर संभावित पैडिंग बाइट्स होते हैं, जो कि भ्रष्ट हो सकते हैं यदि आप समग्र रूप से संरचना को असाइन करते हैं, लेकिन यदि आप केवल व्यक्तिगत रूप से एक्सेस नहीं करते हैं, तो सदस्य, और बाकी तत्व आवंटित ऑब्जेक्ट में अतिरिक्त स्थान हैं जो आप वैसे भी उपयोग करने के लिए स्वतंत्र हैं, जब तक आप संरेखण आवश्यकताओं का पालन करते हैं (और charकोई संरेखण आवश्यकताएं नहीं हैं)।

आप चिंतित हैं कि गद्दी के साथ अतिव्यापी की संभावना struct में बाइट्स किसी भी तरह नाक राक्षसों आह्वान, आप बदल कर भी इस से बच सकते हैं सकता है 1में [1]जो यह सुनिश्चित करता है struct के अंत में कोई गद्दी है कि वहाँ एक मूल्य के साथ। ऐसा करने का एक सरल लेकिन व्यर्थ तरीका यह होगा कि अंत में बिना किसी सरणी को छोड़कर समान सदस्यों के साथ एक संरचना बनाई जाए और s[sizeof struct that_other_struct];सरणी के लिए उपयोग किया जाए। फिर, p->s[i]स्पष्ट रूप से संरचना में सरणी के एक तत्व के i<sizeof struct that_other_structरूप में और के लिए संरचना के अंत के बाद एक पते पर एक चार ऑब्जेक्ट के रूप में परिभाषित किया गया है i>=sizeof struct that_other_struct

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

संपादित करें 2: पैडिंग बाइट्स के साथ ओवरलैप निश्चित रूप से एक मुद्दा नहीं है, मानक के एक और हिस्से के कारण। C के लिए आवश्यक है कि यदि दो संरचनाएं अपने तत्वों की प्रारंभिक अनुगामी में सहमत हों, तो सामान्य प्रारंभिक तत्वों को एक पॉइंटर के माध्यम से या तो टाइप किया जा सकता है। एक परिणाम के रूप में, अगर एक struct के समान struct Tहै, लेकिन एक बड़ा अंतिम सरणी के साथ घोषित किया गया, तत्व s[0]तत्व के साथ मेल खाना करने के लिए होता s[0]में struct T, और इन अतिरिक्त तत्वों की उपस्थिति को प्रभावित नहीं कर सकता है या बड़ा struct के आम तत्व तक पहुँचने से प्रभावित हो करने के लिए एक सूचक का उपयोग कर struct T


4
आप सही हैं कि सूचक अंकगणित की प्रकृति अप्रासंगिक है, लेकिन आप सरणी के घोषित आकार से परे पहुंच के बारे में गलत हैं। देखें N1494 (नवीनतम सार्वजनिक C1x मसौदा) खंड 6.5.6 पैरा 8 - तुम भी ऐसा करने की अनुमति नहीं कर रहे हैं इसके अलावा है कि पिछले एक से अधिक तत्व सरणी के घोषित आकार एक सूचक लेता है, और आप इसे भिन्नता नहीं कर सकते हैं, भले ही यह सिर्फ एक तत्व अतीत है।
zwol

1
@Zack: यह सच है अगर ऑब्जेक्ट एक सरणी है। यह सच नहीं है यदि वस्तु एक ऐसी वस्तु है mallocजिसे किसी सरणी के रूप में एक्सेस किया जा रहा है या यदि यह एक बड़ी संरचना है जिसे एक पॉइंटर के माध्यम से एक छोटी संरचना तक पहुँचा जा रहा है जिसके तत्व एक बड़ी संरचना के तत्वों का एक प्रारंभिक सबसेट हैं, अन्य के बीच मामलों।
आर .. गिटहब स्टॉप हेल्पिंग ICE

6
+1 यदि mallocवह मेमोरी की एक सीमा को आवंटित नहीं करता है जिसे सूचक अंकगणित के साथ एक्सेस किया जा सकता है, तो इसका क्या उपयोग होगा? और अगर p->s[1]है परिभाषित है कि सूचक अंकगणित के लिए वाक्यात्मक चीनी, तो इस सवाल का जवाब केवल reasserts के रूप में मानक से mallocउपयोगी है। चर्चा करने के लिए क्या बचा है? :)
डैनियल इयरविकर

3
आप यह तर्क दे सकते हैं कि यह उतना ही अच्छी तरह से परिभाषित है जितना आप चाहते हैं, लेकिन यह इस तथ्य को नहीं बदलता है कि यह नहीं है। किसी सरणी की सीमा से परे पहुंच के बारे में मानक बहुत स्पष्ट है, और इस सरणी का बाउंड है 1। यह उतना ही सरल है।
ऑर्बिट में लाइटनेस दौड़

3
@ आर .., मुझे लगता है, आपकी धारणा यह है कि समान की तुलना करने वाले दो बिंदुओं का व्यवहार समान होना चाहिए, गलत है। पर विचार करें int m[1]; int n[1]; if(m+1 == n) m[1] = 0;संभालने ifशाखा में प्रवेश कर रहा है। यह यूबी (और शुरू में गारंटी नहीं है n) 6.5.6 पी 8 (अंतिम वाक्य) के अनुसार, जैसा कि मैंने इसे पढ़ा है। संबंधित: 6.5.9 p6 फुटनोट 109 के साथ। (संदर्भ C11 n1570 के लिए हैं।) [...]
mafso

7

हां, यह तकनीकी रूप से अपरिभाषित व्यवहार है।

ध्यान दें, "स्ट्रक्चर हैक" को लागू करने के लिए कम से कम तीन तरीके हैं:

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

(२) सरणी को न्यूनतम कानूनी आकार के साथ घोषित करना - १ (आपका मामला)। इस मामले में p->s[0], सूचक को अंकगणितीय अंकगणित के लिए उपयोग करने और उसका उपयोग करने का प्रयास जो कि परे p->s[1]है अपरिभाषित व्यवहार है। उदाहरण के लिए, डिबगिंग कार्यान्वयन को एम्बेडेड श्रेणी की जानकारी के साथ एक विशेष पॉइंटर का उत्पादन करने की अनुमति है, जो हर बार जब आप एक पॉइंटर से परे बनाने का प्रयास करते हैं, तो वह फंस जाएगा p->s[1]

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


1
में (2), s[1]अपरिभाषित व्यवहार नहीं है। यह वैसा ही है *(s+1), जो कि समान है *((char *)p + offsetof(struct T, s) + 1), जो charकि आवंटित वस्तु में वैध सूचक है ।
आर .. गिटहब स्टॉप हेल्पिंग आईसीई

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

आर: यदि एक सरणी को आकार देने के लिए घोषित किया गया था (केवल इसके लिए foo[]सिंटैक्टिक चीनी नहीं है *foo), तो इसके घोषित आकार और इसके आवंटित आकार के छोटे से परे कोई पहुंच यूबी है, भले ही सूचक अंकगणित कैसे किया गया हो।
zwol

1
@Zack, आप कई चीजों पर गलत हैं। foo[]एक संरचना में के लिए सिंथेटिक चीनी नहीं है *foo; यह एक C99 लचीला सरणी सदस्य है। बाकी के लिए, अन्य उत्तरों पर मेरे उत्तर और टिप्पणियों को देखें।
R .. गिटहब स्टॉप हेल्पिंग ICE

6
मुद्दा यह है कि समिति के कुछ सदस्य चाहते हैं कि यह "हैक" यूबी हो, क्योंकि वे कुछ फेयरीलैंड की कल्पना करते हैं जहां एक सी कार्यान्वयन पॉइंटर सीमा को लागू कर सकता है। बेहतर या बदतर के लिए, हालांकि, ऐसा करना मानक के अन्य भागों के साथ संघर्ष करना होगा - समानता के लिए संकेत की तुलना करने की क्षमता जैसी चीजें (यदि सूचक में ही सीमाएं एन्कोडेड थीं) या आवश्यकता होती है कि कोई भी वस्तु एक काल्पनिक अतिवृद्धि unsigned char [sizeof object]सरणी के माध्यम से सुलभ हो सकती है । मैं अपने दावे से खड़ा हूं कि प्री-सी 99 के लिए लचीले सरणी सदस्य "हैक" में अच्छी तरह से परिभाषित व्यवहार है।
आर .. गिटहब स्टॉप हेल्पिंग ICE

3

मानक काफी स्पष्ट है कि आप किसी सरणी के अंत के पास चीजों तक नहीं पहुंच सकते। (और संकेत के माध्यम से जाना मदद नहीं करता है, क्योंकि आपको सरणी समाप्ति के बाद पिछले एक भी वृद्धि के संकेत की अनुमति नहीं है)।

और "व्यवहार में काम करने के लिए"। मैंने मानक के इस भाग का उपयोग करके gcc / g ++ ऑप्टिमाइज़र देखा है, इस प्रकार इस सी को पूरा करते समय गलत कोड उत्पन्न करता है।


क्या आप एक उदाहरण दे सकते हैं?
ताल

1

यदि कोई कंपाइलर कुछ स्वीकार करता है

टाइपराइफ़ संरचना {
  इंट लेन;
  चार दात [];
};

मुझे लगता है कि यह बहुत स्पष्ट है कि इसकी लंबाई से परे 'डाट' पर एक उपलेख को स्वीकार करने के लिए तैयार होना चाहिए। दूसरी ओर, यदि कोई व्यक्ति कुछ कोड करता है:

टाइपराइफ़ संरचना {
  int जो भी हो;
  चार दात [1];
} MY_STRUCT;

और फिर बाद में किसी और दिन पहुंचता है-> डेटा [x]; मुझे नहीं लगता कि कंपाइलर पता-गणना कोड का उपयोग करने के लिए किसी भी दायित्व के तहत है जो एक्स के बड़े मूल्यों के साथ काम करेगा। मुझे लगता है कि अगर कोई वास्तव में सुरक्षित होना चाहता था, तो उचित प्रतिमान अधिक पसंद आएगा:

#define LARGEST_DAT_SIZE 0xF000
टाइपराइफ़ संरचना {
  int जो भी हो;
  char dat [LARGEST_DAT_SIZE];
} MY_STRUCT;

और उसके बाद (sizeof (MYSTRUCT) -LARGEST_DAT_SIZE + वांछित_अवर_लॉग) बाइट्स का ध्यान रखें (ध्यान रखें कि अगर वांछित_ल्रे_लॉग LGGEST_DAT_SIZE से बड़ा है, तो परिणाम अपरिभाषित हो सकते हैं)।

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

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