"Int * nums = {5, 2, 1, 4}" एक विभाजन दोष का कारण बनता है


81

एक segfault का कारण बनता है, जबकि

ऐसा नहीं करता। अभी:

प्रिंट 5।

इसके आधार पर, मैंने अनुमान लगाया है कि सरणी आरंभीकरण संकेतन, {}, इस डेटा को नेत्रहीन रूप से लोड करता है जो कुछ भी चर बाईं तरफ है। जब यह int [] होता है, तो सरणी वांछित के रूप में भर जाती है। जब यह int * होता है, तो पॉइंटर को 5 से भरा जाता है, और मेमोरी लोकेशन के बाद जहां पॉइंटर को स्टोर किया जाता है, 2, 1, और 4 तक भरा जाता है। इसलिए [5] deref 5 के लिए प्रयास करता है, जिससे सेगफॉल्ट होता है।

अगर मैं गलत हूं, तो कृपया मुझे सुधारें। और अगर मैं सही हूं, तो कृपया विस्तार से बताएं, क्योंकि मुझे समझ नहीं आ रहा है कि ऐरे इनिशियललाइजर्स जिस तरह से काम करते हैं।



3
सक्षम सभी चेतावनियों के साथ संकलित करें और आपके संकलक को यह बताना चाहिए कि क्या होता है।
Jabberwocky

1
@GSerg एक डुप्लिकेट के पास कहीं भी नहीं है। इस प्रश्न में कोई सरणी सूचक नहीं है। हालांकि उस पोस्ट में कुछ उत्तर यहां के लोगों के समान हैं।
लंडिन

2
@ लुंडिन मैं 30% सुनिश्चित था, इसलिए मैंने मतदान बंद नहीं किया, केवल लिंक पोस्ट किया।
GSerg

3
-pedantic-errorsझंडा के साथ जीसीसी चलाने की आदत डालें और डायग्नोस्टिक देखें। int *nums = {5, 2, 1, 4};मान्य नहीं है
AnT

जवाबों:


113

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

उदाहरण के लिए आप लिख सकते हैं int x = {0};, जो पूरी तरह से बराबर है int x = 0;

इसलिए जब आप लिखते int *nums = {5, 2, 1, 4};हैं तो आप वास्तव में एक सिंगल पॉइंटर वैरिएबल को इनिशियलाइज़र सूची दे रहे होते हैं। हालांकि, यह सिर्फ एक एकल चर है, इसलिए इसे केवल पहला मान 5 सौंपा जाएगा, बाकी सूची को नजरअंदाज कर दिया गया है (वास्तव में मुझे नहीं लगता है कि अतिरिक्त initializers के साथ कोड भी एक सख्त संकलक के साथ संकलन करना चाहिए) - यह नहीं करता है स्मृति पर लिखा जाना। कोड के बराबर है int *nums = 5;। जिसका अर्थ है, पतेnums पर इंगित करना चाहिए । 5

इस बिंदु पर आपको पहले से ही दो संकलक चेतावनी / त्रुटियाँ मिलनी चाहिए:

  • एक डाली के बिना सूचक को पूर्णांक निर्दिष्ट करना।
  • प्रारंभिक सूची में अतिरिक्त तत्व।

और फिर निश्चित रूप से कोड दुर्घटनाग्रस्त हो जाएगा और जल जाएगा क्योंकि 5सबसे अधिक संभावना है कि आपको एक वैध पता नहीं है जिसके साथ आपको अनुमति दी गई है nums[0]

एक साइड नोट के रूप में, आपको विनिर्देशक के printfसाथ पतों को इंगित करना चाहिए %pया अन्यथा आप अपरिभाषित व्यवहार कर रहे हैं।


मुझे पूरा यकीन नहीं है कि आप यहां क्या करने की कोशिश कर रहे हैं, लेकिन अगर आप किसी पॉइंट पर पॉइंट करने के लिए पॉइंटर सेट करना चाहते हैं, तो आपको यह करना चाहिए:

या यदि आप बिंदुओं की एक सरणी बनाना चाहते हैं:


संपादित करें

कुछ शोधों के बाद मैं कह सकता हूं कि "अतिरिक्त तत्व आरंभीकरण सूची" वास्तव में वैध सी नहीं है - यह एक जीसीसी विस्तार है

मानक 6.7.9 प्रारंभिक कहता है (जोर मेरा):

2 कोई भी इनिलाइज़र उस ऑब्जेक्ट के लिए एक मूल्य प्रदान करने का प्रयास नहीं करेगा जो कि इनिशियलाइज़ किए जाने के भीतर निहित नहीं है।

/ - /

11 एक स्केलर के लिए इनिशलाइज़र एक एकल अभिव्यक्ति होगी, वैकल्पिक रूप से ब्रेसिज़ में संलग्न होगी। वस्तु का प्रारंभिक मूल्य अभिव्यक्ति (रूपांतरण के बाद) है; साधारण असाइनमेंट के लिए एक ही प्रकार की बाधाएं और रूपांतरण, स्केलर के प्रकार को अपने घोषित प्रकार के अयोग्य संस्करण के रूप में लेते हैं।

"स्केलर टाइप" एकल चर का संदर्भ देने वाला एक मानक शब्द है जो सरणी, संरचना या संघ प्रकार का नहीं होता है (जिन्हें "कुल प्रकार" कहा जाता है)।

तो सादा अंग्रेजी में मानक कहता है: "जब आप एक वैरिएबल को इनिशियलाइज़ करते हैं, तो इनिशियल एक्सप्रेशन के चारों ओर कुछ अतिरिक्त ब्रेसिज़ में टॉस करने के लिए स्वतंत्र महसूस करें, सिर्फ इसलिए कि आप कर सकते हैं।"


11
अंदर मौजूद किसी एकल मान के साथ स्केलर ऑब्जेक्ट को इनिशियलाइज़ करने की क्षमता के बारे में "बेवकूफ" कुछ भी नहीं है {}। इसके विपरीत, यह सी भाषा के सबसे महत्वपूर्ण और सुविधाजनक मुहावरों में से एक की सुविधा देता है - { 0 }सार्वभौमिक शून्य-इनिलाइज़र के रूप में। C में सब कुछ शून्य-प्रारंभिक किया जा सकता है = { 0 }। टाइप-इंडिपेंडेंट कोड लिखने के लिए यह बहुत जरूरी है।
एनटी

3
@AnT "यूनिवर्सल जीरो-इनिलाइज़र" जैसी कोई चीज़ नहीं है। समुच्चय के मामले में, {0}इसका मतलब केवल पहली वस्तु को शून्य से प्रारंभ करना है और बाकी वस्तुओं को प्रारंभिक रूप से निर्धारित करना है जैसे कि उनके पास स्थिर भंडारण अवधि थी। मैं कहूंगा कि यह संयोग से कुछ "सार्वभौमिक शुरुआती" के जानबूझकर भाषा डिजाइन के बजाय है, क्योंकि {1}सभी वस्तुओं को 1 से प्रारंभ नहीं करता है।
लुंडिन

3
@ लुंडिन C11 6.5.16.1/1 कवर p = 5;(सूचीबद्ध मामलों में से कोई भी सूचक को पूर्णांक देने के लिए नहीं मिला है); और 6.7.9 / 11 का कहना है कि असाइनमेंट के लिए बाधाओं का उपयोग आरंभीकरण के लिए भी किया जाता है।
MM

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

2
@ लुंडिन: यह मेरे लिए भी पूरी तरह से अस्पष्ट है कि आपकी टिप्पणी {1}का विषय के साथ क्या संबंध है। किसी ने कभी भी यह दावा नहीं किया कि प्रत्येक और हर सदस्य के लिए एक बहु-आरंभक के रूप में {0}व्याख्या करता है 0
एनटी

28

दृष्टांत 1

क्यों यह एक segfault करता है?

आपने numsint के लिए एक सूचक के रूप में घोषित किया - जो कि स्मृति में एक पूर्णांक numsके पते को धारण करना है ।

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

या तो मामले में (एक पॉइंटर या इंट चर के लिए कई मान निर्दिष्ट करें), फिर क्या होता है कि चर को पहला मान मिलेगा 5, जबकि शेष मानों की अनदेखी की जाती है। यह कोड अनुपालन करता है लेकिन आपको प्रत्येक अतिरिक्त मूल्य के लिए चेतावनी मिलेगी जो असाइनमेंट में नहीं है:

warning: excess elements in scalar initializer

पॉइंटर वैरिएबल में कई मानों को असाइन करने के मामले में, जब आप एक्सेस करते हैं, तो प्रोग्राम सेगफॉल्ट करता है nums[0], जिसका अर्थ है कि आप एड्रेस 5 में जो कुछ भी स्टोर कर रहे हैं, उसे डिरेल कर रहे हैं । आपने numsइस मामले में सूचक के लिए कोई मान्य मेमोरी आवंटित नहीं की थी ।

यह ध्यान देने योग्य होगा कि इंट वैरिएबल के लिए कई मान निर्दिष्ट करने के मामले के लिए कोई segfault नहीं है (आप यहां किसी भी अमान्य पॉइंटर को डीरफेर नहीं कर रहे हैं)।


SCENARIO 2

यह एक सेगफॉल्ट नहीं करता है, क्योंकि आप कानूनी तौर पर स्टैक में 4 इन्टस की एक सरणी आवंटित कर रहे हैं।


SCENARIO 3

यह एक उम्मीद के मुताबिक नहीं करता है, क्योंकि आप खुद ही पॉइंटर के मूल्य को प्रिंट कर रहे हैं - ऐसा नहीं है कि यह डेरेफेरिंग (जो अमान्य मेमोरी एक्सेस है)।


अन्य

जब भी आप इस तरह एक पॉइंटर के मूल्य को हार्डकोड करते हैं, तो यह लगभग हमेशा सेगफॉल्ट के लिए बर्बाद होता है (क्योंकि यह निर्धारित करने के लिए ऑपरेटिंग सिस्टम का काम है कि कौन सी मेमोरी किस स्थान तक पहुंच सकती है)।

इसलिए अंगूठे का एक नियम हमेशा कुछ आवंटित चर के पते के लिए एक सूचक को इनिशियलाइज़ करना है , जैसे:

या,


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

3
"यह मान्य है" - अतिरिक्त आरंभीकरण को अनदेखा करना एक जीसीसी विस्तार है; मानक C में जिसे अनुमति नहीं है
MM

1
@ TheNate - हाँ आप सही हैं। मैंने आपकी टिप्पणी के आधार को संपादित किया - धन्यवाद।
आर्टम

@MM - यह इंगित करने के लिए धन्यवाद। मैंने उसे हटाने के लिए संपादन किया।
आर्टम

25

int *nums = {5, 2, 1, 4};बीमार कोड है। एक GCC एक्सटेंशन है जो इस कोड को उसी के समान मानता है:

मेमोरी एड्रेस 5 को पॉइंटर बनाने का प्रयास। (यह मेरे लिए एक उपयोगी एक्सटेंशन की तरह नहीं लगता है, लेकिन मुझे लगता है कि डेवलपर बेस इसे चाहता है)।

इस व्यवहार से बचने के लिए (या कम से कम, एक चेतावनी प्राप्त करें) आप मानक मोड में संकलित कर सकते हैं, उदा -std=c11 -pedantic

मान्य कोड का एक वैकल्पिक रूप होगा:

जो एक ही भंडारण अवधि के एक उत्परिवर्ती शाब्दिक बिंदु पर इंगित करता है nums। हालांकि, int nums[]संस्करण आमतौर पर बेहतर होता है क्योंकि यह कम भंडारण का उपयोग करता है, और आप यह sizeofपता लगाने के लिए उपयोग कर सकते हैं कि सरणी कितनी लंबी है।


क्या यौगिक-शाब्दिक रूप में सरणी को कम से कम लंबे समय तक भंडारण जीवनकाल की गारंटी होगी nums?
सुपरकैट

@supercat हाँ, यह स्वचालित है यदि अंक स्वचालित है, और स्थिर है यदि अंक स्थिर है
MM

@MM: क्या यह लागू होगा, भले ही numsकिसी फ़ंक्शन के भीतर एक स्थिर चर घोषित किया गया हो, या कंपाइलर सरणी के जीवनकाल को एन्क्लोजिंग ब्लॉक की सीमा तक सीमित करने का हकदार होगा, भले ही वह एक स्थिर चर को सौंपा जा रहा हो?
सुपरकैट

@ सुपरकैट हाँ (पहला बिट)। दूसरे विकल्प का मतलब होगा यूबी दूसरी बार फ़ंक्शन को कॉल किया जाता है (चूंकि स्थिर चर केवल पहली कॉल पर आरंभिक होते हैं)
एमएम

12

numsप्रकार का सूचक है int। इसलिए आपको इस बिंदु को कुछ मान्य मेमोरी स्थान पर बनाना चाहिए। num[0]आप कुछ यादृच्छिक स्मृति स्थान और इसलिए विभाजन दोष को रोकने का प्रयास कर रहे हैं।

हां पॉइंटर 5 का मान रखता है और आप इसे डीरेल करने की कोशिश कर रहे हैं जो आपके सिस्टम पर अपरिभाषित व्यवहार है। (ऐसा लगता है कि 5आपके सिस्टम पर कोई मान्य मेमोरी लोकेशन नहीं है)

जहाँ तक

एक वैध घोषणा है जहां आप कह रहे हैं कि numsएक प्रकार की सरणी है intऔर स्मृति को आरंभीकरण के दौरान पारित किए गए तत्वों की संख्या के आधार पर आवंटित किया गया है।


1
"हाँ पॉइंटर का मान 5 है और आप इसे निष्क्रिय करने की कोशिश कर रहे हैं जो अपरिभाषित व्यवहार है।" बिल्कुल नहीं, यह पूरी तरह से ठीक है और अच्छी तरह से परिभाषित व्यवहार है। लेकिन ओपी जिस सिस्टम का उपयोग कर रहा है, वह वैध मेमोरी एड्रेस नहीं है, इसलिए क्रैश।
लुंडिन

@ लुंडिन सहमत। लेकिन मुझे लगता है कि ओपी कभी नहीं जानता था कि 5 एक वैध मेमोरी स्थान है इसलिए मैंने उन पंक्तियों पर बात की। आशा है कि संपादित मदद करता है
गोपी

ऐसा होना चाहिए? int *nums = (int[]){5, 2, 1, 4};
इस्लाम अज़ब

10

असाइन करके {5, 2, 1, 4}

आप 5 से असाइन कर रहे हैं nums(एक अंतर्निहित टाइपकास्ट के बाद इंट से पॉइंटर से इंट में)। इसका उल्लेख करने से मेमोरी लोकेशन पर एक्सेस कॉल हो जाता है 0x5। आपके प्रोग्राम को एक्सेस करने की अनुमति नहीं दी जा सकती है।

प्रयत्न

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