C में पॉइंटर तुलना कैसे काम करती है? क्या पॉइंटर्स की तुलना करना ठीक है जो एक ही एरे की ओर इशारा नहीं करते हैं?


33

K & R में (C प्रोग्रामिंग लैंग्वेज द्वितीय संस्करण) अध्याय 5 मैंने निम्नलिखित पढ़ा:

सबसे पहले, कुछ परिस्थितियों में पॉइंटर्स की तुलना की जा सकती है। तो pऔर qएक ही सरणी के सदस्यों के लिए बिंदु, तो संबंधों की तरह ==, !=, <, >=ठीक से, आदि काम करते हैं।

जिसका अर्थ यह प्रतीत होता है कि केवल उसी व्यूह की ओर संकेत करने वाले बिंदुओं की तुलना की जा सकती है।

हालाँकि जब मैंने इस कोड की कोशिश की

    char t = 't';
    char *pt = &t;
    char x = 'x';
    char *px = &x;

    printf("%d\n", pt > px);

1 स्क्रीन पर मुद्रित किया जाता है।

सबसे पहले, मैंने सोचा कि मैं अपरिभाषित या कुछ प्रकार या त्रुटि प्राप्त करूंगा, क्योंकि ptऔर pxएक ही सरणी (कम से कम मेरी समझ में) की ओर इशारा नहीं कर रहे हैं।

pt > pxइसलिए भी क्योंकि दोनों पॉइंटर्स स्टैक पर स्टोर किए गए वेरिएबल्स की ओर इशारा कर रहे हैं, और स्टैक नीचे बढ़ता है, इसलिए इसके बारे में मेमोरी एड्रेस tअधिक है x? कौन सा pt > pxसच क्यों है?

जब मलोको को लाया जाता है तो मैं और अधिक भ्रमित हो जाता हूं। अध्याय 8.7 में K & R में भी लिखा गया है:

हालांकि, अभी भी एक धारणा है, विभिन्न बिंदुओं पर लौटे sbrkबिंदुओं की तुलना सार्थक रूप से की जा सकती है। यह मानक द्वारा गारंटी नहीं है जो केवल एक सरणी के भीतर सूचक तुलना की अनुमति देता है। इस प्रकार यह संस्करण mallocकेवल मशीनों के बीच पोर्टेबल है, जिसके लिए सामान्य सूचक तुलना सार्थक है।

मेरे पास पॉइंटर्स की तुलना करने वाला कोई मुद्दा नहीं था, जो कि ढेर पर पॉइंटर्स के लिए स्पेस मैलोकेड को इंगित करता था जो स्टैक चर को इंगित करता था।

उदाहरण के लिए, निम्न कोड ने ठीक काम किया, 1मुद्रित होने के साथ :

    char t = 't';
    char *pt = &t;
    char *px = malloc(10);
    strcpy(px, pt);
    printf("%d\n", pt > px);

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

फिर भी, मैं K & R में जो कुछ पढ़ रहा हूं, उससे भ्रमित हूं।

कारण मैं पूछ रहा हूँ क्योंकि मेरे प्रोफेसर है। वास्तव में इसे एक परीक्षा प्रश्न बना दिया। उन्होंने निम्नलिखित कोड दिया:

struct A {
    char *p0;
    char *p1;
};

int main(int argc, char **argv) {
    char a = 0;
    char *b = "W";
    char c[] = [ 'L', 'O', 'L', 0 ];

   struct A p[3];
    p[0].p0 = &a;
    p[1].p0 = b;
    p[2].p0 = c;

   for(int i = 0; i < 3; i++) {
        p[i].p1 = malloc(10);
        strcpy(p[i].p1, p[i].p0);
    }
}

ये क्या मूल्यांकन करते हैं:

  1. p[0].p0 < p[0].p1
  2. p[1].p0 < p[1].p1
  3. p[2].p0 < p[2].p1

जवाब है 0, 1और 0

(मेरे प्रोफेसर परीक्षा में डिस्क्लेमर शामिल करते हैं कि प्रश्न उबंटू लिनक्स 16.04, 64-बिट संस्करण प्रोग्रामिंग वातावरण के लिए हैं)

(संपादक का ध्यान दें: यदि SO ने अधिक टैग की अनुमति दी है, तो अंतिम भाग , , और शायद को वारंट करेगा । यदि प्रश्न / वर्ग का बिंदु विशेष रूप से पोर्टेबल सी के बजाय निम्न-स्तरीय OS कार्यान्वयन विवरण था)


17
आप शायद भ्रमित कर रहे हैं क्या है वैध में Cक्या है के साथ सुरक्षित में Cएक ही प्रकार के दो बिंदुओं की तुलना हमेशा की जा सकती है (उदाहरण के लिए समानता की जाँच), हालांकि, सूचक अंकगणितीय और तुलना का उपयोग करना >और <केवल तब ही सुरक्षित है जब किसी दिए गए सरणी (या मेमोरी ब्लॉक) के भीतर उपयोग किया जाता है
एड्रियन मोल

13
एक तरफ के रूप में, आपको C & R से C नहीं सीखना चाहिए । एक शुरुआत के लिए, भाषा तब से बहुत सारे बदलावों से गुजर रही है । और, ईमानदार होने के लिए, उदाहरण के लिए कोड उस समय से था जब पठनीयता के बजाय थकाऊपन का महत्व था।
paxdiablo

5
नहीं, यह काम करने की गारंटी नहीं है। यह खंडित मेमोरी मॉडल वाली मशीनों पर अभ्यास में विफल हो सकता है। देखें क्या C का std के बराबर :: C ++ से कम है? अधिकांश आधुनिक मशीनों पर, यह यूबी के बावजूद काम करने के लिए होगा।
पीटर कॉर्ड्स

6
@ अदम: बंद, लेकिन यह वास्तव में यूबी है (जब तक कि कंपाइलर ओपी जीसीसी का उपयोग नहीं कर रहा था, इसे परिभाषित करने का चयन करता है। यह हो सकता है)। लेकिन यूबी का मतलब "निश्चित रूप से विस्फोट" नहीं है; यूबी के लिए संभावित व्यवहारों में से एक है, जिस तरह से आप उम्मीद कर रहे हैं !! यह वही है जो यूबी को इतना बुरा बनाता है; यह डिबग बिल्ड में सही काम कर सकता है और अनुकूलन सक्षम, या इसके विपरीत में विफल हो सकता है, या आसपास के कोड के आधार पर टूट सकता है। अन्य बिंदुओं की तुलना करना अभी भी आपको एक उत्तर देगा, लेकिन भाषा परिभाषित नहीं करती है कि उस उत्तर का क्या अर्थ होगा (यदि कुछ भी)। नहीं, दुर्घटनाग्रस्त होने की अनुमति है। यह वास्तव में यूबी है।
पीटर कॉर्ड्स

3
@ अदम: ओह हाँ, मेरी टिप्पणी के पहले भाग के बारे में कोई बात नहीं, मैंने आपको गलत समझा। लेकिन आप दावा करते हैं कि अन्य बिंदुओं की तुलना करना अभी भी आपको जवाब देगा । यह सच नहीं है। यह एक अनिर्दिष्ट परिणाम होगा , पूर्ण यूबी नहीं। यूबी बहुत खराब है और इसका मतलब है कि यदि आपका प्रोग्राम उन इनपुट के साथ उस स्थिति में पहुंचता है (वास्तव में ऐसा होने से पहले या उसके बाद किसी भी बिंदु पर) सेगफॉल्ट या SIGILL हो सकता है। (केवल x86-64 पर प्रशंसनीय है यदि यूबी संकलन समय पर दिखाई दे रहा है, लेकिन सामान्य तौर पर कुछ भी हो सकता है।) यूबी के बिंदु का एक हिस्सा एएसएम उत्पन्न करते समय कंपाइलर को "असुरक्षित" धारणा बनाने देना है।
पीटर कॉर्ड्स

जवाबों:


33

के अनुसार सी 11 मानक , संबंधपरक ऑपरेटर <, <=, >, और >=केवल संकेत पर एक ही सरणी या struct वस्तु के तत्वों के लिए इस्तेमाल किया जा सकता है। यह खंड 6.5.8p5 में लिखा गया है:

जब दो बिंदुओं की तुलना की जाती है, तो परिणाम इंगित किए गए ऑब्जेक्ट के पता स्थान में सापेक्ष स्थानों पर निर्भर करता है। यदि दो प्रकार के ऑब्जेक्ट को टाइप करने के लिए दोनों एक ही ऑब्जेक्ट को इंगित करते हैं, या दोनों एक ही एरे ऑब्जेक्ट के अंतिम तत्व को इंगित करते हैं, तो वे बराबर की तुलना करते हैं। यदि इंगित की गई वस्तुएं समान एग्रीगेट ऑब्जेक्ट के सदस्य हैं, तो बाद में घोषित किए गए स्ट्रक्चर मेंबर्स के लिए पॉइंटर्स स्ट्रक्चर में पहले से घोषित सदस्यों की तुलना में पॉइंटर्स की तुलना में अधिक हैं, और बड़े सबस्क्रिप्ट वैल्यू वाले एलीमेंट तत्वों के पॉइंटर्स एक ही एरे के तत्वों से पॉइंटर्स की तुलना में बड़े होते हैं। निचले सबस्क्रिप्ट मानों के साथ। समान यूनियन ऑब्जेक्ट के सदस्यों के सभी संकेत बराबर की तुलना करते हैं।

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

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

एक उदाहरण के रूप में, 8086 वास्तविक मोड में चलने वाले एक x86 प्रोसेसर में 16-बिट सेगमेंट का उपयोग करके एक खंडित मेमोरी मॉडल है और 20-बिट एड्रेस बनाने के लिए 16-बिट ऑफसेट है। तो इस स्थिति में एक पता पूर्णांक में परिवर्तित नहीं होता है।

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

हालाँकि, यहां तक ​​कि ==और !=आप कुछ अप्रत्याशित अभी तक अच्छी तरह से परिभाषित परिणाम प्राप्त कर सकते हैं। देखें कि क्या असंबद्ध बिंदुओं की समानता की तुलना सत्य का मूल्यांकन कर सकती है? इस पर अधिक जानकारी के लिए।

आपके प्रोफेसर द्वारा दिए गए परीक्षा प्रश्न के बारे में, यह कई त्रुटिपूर्ण धारणाएँ बनाता है:

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

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

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


3
@Shisui यहां तक ​​कि दिया, आप अभी भी परिणामों पर निर्भर नहीं होना चाहिए। जब अनुकूलन की बात आती है तो कंपाइलर बहुत आक्रामक हो सकते हैं और ऐसा करने के अवसर के रूप में अपरिभाषित व्यवहार का उपयोग करेंगे। यह संभव है कि एक अलग संकलक और / या विभिन्न अनुकूलन सेटिंग्स का उपयोग करने से अलग-अलग आउटपुट उत्पन्न हो सकते हैं।
dbush

2
@ यह: सामान्य तौर पर यह एक फ्लैट मेमोरी मॉडल के साथ मशीनों पर काम करने के लिए होगा, जैसे कि x86-64। ऐसी प्रणालियों के लिए कुछ संकलक भी अपने प्रलेखन में व्यवहार को परिभाषित कर सकते हैं। लेकिन यदि नहीं, तो संकलन-समय-दृश्यमान यूबी के कारण "पागल" व्यवहार हो सकता है। (व्यवहार में मुझे नहीं लगता कि कोई चाहता है कि इसलिए यह कुछ मुख्यधारा के कंपाइलर नहीं हैं और "तोड़ने की कोशिश करें"।)
पीटर कॉर्ड्स

1
जैसे यदि एक कंपाइलर देखता है कि निष्पादन का एक मार्ग परिणाम और एक स्थानीय चर (स्वचालित भंडारण, यानी स्टैक) के <बीच ले जाएगा malloc, तो यह मान सकता है कि निष्पादन का मार्ग कभी नहीं लिया गया है और बस पूरे फ़ंक्शन को ud2निर्देश के लिए संकलित करता है (एक अवैध उठाता है -इनस्ट्रक्शन अपवाद जिसे कर्नेल प्रक्रिया में एक SIGILL पहुंचाकर संभाल लेगा)। जीसीसी / क्लैंग यूबी के अन्य प्रकारों के लिए अभ्यास करते हैं, जैसे कि एक गैर- voidसमारोह के अंत में गिरना । godbolt.org अभी नीचे है, ऐसा लगता है, लेकिन कॉपी / पेस्ट करने की कोशिश करें int foo(){int x=2;}और एक कमी को नोट करेंret
पीटर कॉर्ड्स

4
@ यह: टीएल: डीआर: यह पोर्टेबल सी नहीं है, इस तथ्य के बावजूद कि यह x86-64 लिनक्स पर ठीक काम करने के लिए होता है। तुलना के परिणामों के बारे में धारणा बनाना अभी पागल है, हालांकि। यदि आप मुख्य थ्रेड में नहीं हैं, तो आपके थ्रेड स्टैक को गतिशील रूप mallocसे ओएस से अधिक मेमोरी प्राप्त करने के लिए उसी तंत्र का उपयोग करके आवंटित किया गया है , इसलिए यह मानने का कोई कारण नहीं है कि आपके स्थानीय संस्करण (थ्रेड स्टैक) mallocगतिशील रूप से आवंटित किए गए हैं। भंडारण।
पीटर कॉर्ड्स

2
@PeterCordes: व्यवहार के विभिन्न पहलुओं को "वैकल्पिक रूप से परिभाषित" के रूप में पहचानने की आवश्यकता है, जैसे कि कार्यान्वयन उन्हें अपने अवकाश पर परिभाषित कर सकते हैं या नहीं, लेकिन परीक्षण योग्य फैशन (जैसे पूर्वनिर्धारित मैक्रो) में इंगित करना चाहिए यदि वे ऐसा नहीं करते हैं। इसके अतिरिक्त, यह दर्शाने के बजाय कि किसी भी स्थिति में जहां अनुकूलन का प्रभाव "अपरिभाषित व्यवहार" के रूप में अवलोकन योग्य होगा, यह कहना कहीं अधिक उपयोगी होगा कि अनुकूलनकर्ता व्यवहार के कुछ पहलुओं को "गैर-अवलोकन योग्य" मान सकते हैं यदि वे संकेत करते हैं कि वे ऐसा करो। उदाहरण के लिए, दिया गया int x,y;, एक कार्यान्वयन ...
सुपरकैट

12

एक ही प्रकार के दो अलग-अलग सरणियों की तुलना करने वाले बिंदुओं के साथ प्राथमिक मुद्दा यह है कि सरणियों को स्वयं एक विशेष सापेक्ष स्थिति में नहीं रखा जाना चाहिए - एक दूसरे से पहले और बाद में समाप्त हो सकता है।

सबसे पहले, मैंने सोचा कि मुझे अपरिभाषित या किसी प्रकार या त्रुटि मिलेगी, क्योंकि pt एक px समान सरणी (कम से कम मेरी समझ में) की ओर इशारा नहीं कर रहे हैं।

नहीं, परिणाम कार्यान्वयन और अन्य अप्रत्याशित कारकों पर निर्भर है।

यह भी pt> px है क्योंकि दोनों संकेत स्टैक पर संग्रहीत चर की ओर इशारा कर रहे हैं, और स्टैक नीचे बढ़ता है, इसलिए टी का मेमोरी पता x की तुलना में अधिक है? यही कारण है कि pt> px सच है?

जरूरी नहीं कि एक स्टैक हो । जब यह मौजूद होता है, तो इसे बढ़ने की जरूरत नहीं है। यह बड़ा हो सकता है। यह कुछ विचित्र तरीके से गैर-संक्रामक हो सकता है।

इसके अलावा, मुझे लगता है कि दो बिंदुओं के बीच सूचक अंकगणित ठीक है, कोई फर्क नहीं पड़ता कि वे व्यक्तिगत रूप से कहां इंगित करते हैं क्योंकि अंकगणित सिर्फ मेमोरी का उपयोग करके पॉइंटर्स स्टोर को संबोधित करता है।

आइए देखें सी विनिर्देश , which6.5.8 पृष्ठ 85 पर जो संबंधपरक ऑपरेटरों (यानी आपके द्वारा उपयोग किए जाने वाले तुलनात्मक ऑपरेटर) पर चर्चा करता है। ध्यान दें कि यह प्रत्यक्ष !=या ==तुलना पर लागू नहीं होता है ।

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

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

अंतिम वाक्य महत्वपूर्ण है। जबकि मैंने अंतरिक्ष को बचाने के लिए कुछ असंबंधित मामलों में कटौती की है, एक ऐसा मामला है जो हमारे लिए महत्वपूर्ण है: दो सरणियाँ, समान संरचना / कुल वस्तु 1 का हिस्सा नहीं हैं , और हम उन दो सरणियों के बिंदुओं की तुलना कर रहे हैं। यह अपरिभाषित व्यवहार है

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

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


1
इससे भी महत्वपूर्ण बात यह है कि एक ही फ़ंक्शन में परिभाषित tऔर xहोने के कारण, इस बात के बारे में कुछ भी मानने का शून्य कारण नहीं है कि x86-64 को लक्षित करने वाला कंपाइलर इस फ़ंक्शन के लिए स्टैक फ्रेम में स्थानीय लोगों को कैसे रखेगा। नीचे की ओर बढ़ते हुए ढेर का एक फ़ंक्शन में चर के घोषणा क्रम से कोई लेना-देना नहीं है। यहां तक ​​कि अलग-अलग कार्यों में, यदि कोई दूसरे में इनलाइन कर सकता है, तो "बच्चे" फ़ंक्शन के स्थानीय लोग अभी भी माता-पिता के साथ मिश्रण कर सकते हैं।
पीटर कॉर्ड्स

1
आपका कंपाइलर पूरे फ़ंक्शन को ऑप्टिमाइज़ कर सकता है जिसमें दृश्य साइड इफेक्ट्स शामिल हैं। ओवरस्टैटमेंट नहीं: यूबी के अन्य प्रकारों के लिए (जैसे कि एक गैर- voidफ़ंक्शन के अंत में गिरना ) जी ++ और क्लैंग ++ वास्तव में ऐसा करते हैं: Godbolt.org/z-g5vesB वे यह मान लें कि निष्पादन का मार्ग नहीं लिया गया है क्योंकि यह यूबी की ओर जाता है, और ऐसे किसी भी बुनियादी ब्लॉक को अवैध निर्देश पर संकलित करता है। या बिल्कुल भी निर्देश नहीं, बस चुपचाप जो कुछ भी आस के माध्यम से गिर रहा है अगर उस फ़ंक्शन को कभी भी बुलाया गया था। (किसी कारण के लिए gccयह नहीं करता है, केवल g++)।
पीटर कॉर्ड्स

6

फिर पूछा क्या

p[0].p0 < p[0].p1
p[1].p0 < p[1].p1
p[2].p0 < p[2].p1

का मूल्यांकन करें। उत्तर 0, 1, और 0 है।

इन सवालों को कम:

  1. ढेर के ऊपर या नीचे ढेर है।
  2. कार्यक्रम के स्ट्रिंग शाब्दिक खंड के ऊपर या नीचे ढेर है।
  3. समान [१]।

और तीनों का उत्तर "कार्यान्वयन परिभाषित" है। आपके प्रोफेसर के प्रश्न फर्जी हैं; उन्होंने इसे पारंपरिक यूनिक्स लेआउट में आधारित किया है:

<empty>
text
rodata
rwdata
bss
< empty, used for heap >
...
stack
kernel

लेकिन कई आधुनिक इकाइयां (और वैकल्पिक प्रणाली) उन परंपराओं के अनुरूप नहीं हैं। जब तक कि उन्होंने "1992 के अनुसार" के साथ इस सवाल को न उठा दिया हो; eval पर -1 देना सुनिश्चित करें।


3
लागू नहीं परिभाषित, अपरिभाषित! इसे इस तरह से सोचें, पूर्व कार्यान्वयन के बीच भिन्न हो सकते हैं लेकिन कार्यान्वयन को यह दस्तावेज़ करना चाहिए कि व्यवहार कैसे तय किया जाता है। उत्तरार्द्ध का मतलब है कि व्यवहार किसी भी तरीके से भिन्न हो सकता है और कार्यान्वयन आपको स्क्वैट को बताने की जरूरत नहीं है :-)
paxdiablo

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

एक और कार्यान्वयन परिभाषित पहलू है; सूचक तुलना पर हस्ताक्षर किए जाते हैं , इसलिए मशीन / ओएस / संकलक के आधार पर, कुछ पते नकारात्मक के रूप में व्याख्या किए जा सकते हैं। उदाहरण के लिए, एक 32 बिट मशीन, जिसने स्टैक को 0xc << 28 पर रखा था, संभवतः हीप या रॉडाटा की तुलना में कम पते पर स्वचालित चर दिखाएगा।
19

1
@mevets: क्या मानक किसी भी स्थिति को निर्दिष्ट करता है जिसमें तुलनाओं में संकेतकर्ताओं की हस्ताक्षरनीयता देखने योग्य होगी? मुझे उम्मीद है कि अगर 16-बिट प्लेटफॉर्म 32768 बाइट्स से अधिक ऑब्जेक्ट की अनुमति देता है, और arr[]ऐसी कोई वस्तु है, तो मानक जनादेश देगा जो कि हस्ताक्षरित पॉइंटर तुलना की arr+32768तुलना में अधिक से अधिक arrतुलना करेगा अन्यथा रिपोर्ट करेगा।
Supercat

मुझे नहीं पता; C मानक डांटे के नौवें चक्र में परिक्रमा कर रहा है, इच्छामृत्यु के लिए प्रार्थना कर रहा है। ओपी ने विशेष रूप से K & R और एक परीक्षा प्रश्न का संदर्भ दिया। #UB एक आलसी कार्य समूह से मलबे है।
mevets

1

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

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

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

ऐसी स्थिति के उदाहरण के रूप में जहां समानता की तुलना गैर-कानूनी रूप से gcc और दबंगों के साथ व्यवहार करती है, पर विचार करें:

extern int x[],y[];
int test(int i)
{
    int *p = y+i;
    y[0] = 4;
    if (p == x+10)
        *p = 1;
    return y[0];
}

क्लैंग और जीसीसी दोनों ऐसे कोड उत्पन्न करेंगे जो हमेशा 4 रिटर्न देंगे भले ही xदस तत्व हो, yतुरंत इसका अनुसरण करता है, और iशून्य शून्य है जिसके परिणामस्वरूप तुलना सही है और p[0]मूल्य 1 के साथ लिखा जा रहा है। मुझे लगता है कि क्या होता है कि अनुकूलन का एक पास फिर से लिखता है फ़ंक्शन के साथ यद्यपि *p = 1;प्रतिस्थापित किया गया था x[10] = 1;। बाद वाला कोड बराबर होगा यदि कंपाइलर *(x+10)के बराबर व्याख्या की जाती है *(y+i), लेकिन दुर्भाग्य से एक डाउनस्ट्रीम ऑप्टिमाइज़ेशन चरण मानता है कि एक एक्सेस को x[10]केवल तब परिभाषित किया जाएगा जब xकम से कम 11 तत्व होते हैं, जिससे उस एक्सेस को प्रभावित करना असंभव हो जाता है y

यदि संकलक उस "रचनात्मक" को सूचक समानता परिदृश्य के साथ प्राप्त कर सकते हैं जो मानक द्वारा वर्णित है, तो मैं उन पर भरोसा नहीं करूंगा कि उन मामलों में और अधिक रचनात्मक होने से बचना चाहिए जहां मानक आवश्यकताओं को लागू नहीं करता है।


0

यह सरल है: तुलना करने वाले पॉइंटर्स का कोई मतलब नहीं है क्योंकि ऑब्जेक्ट के लिए मेमोरी लोकेशन कभी भी उसी क्रम में होने की गारंटी नहीं है जैसा आपने उन्हें घोषित किया था। अपवाद एरेज़ है। & सरणी [0] & सरणी से कम है [1]। Ks और R जो बताते हैं। व्यवहार में, सदस्य के पते भी उसी क्रम में हैं, जिस क्रम में आप उन्हें मेरे अनुभव की घोषणा करते हैं। उस पर कोई गारंटी नहीं है .... यदि आप बराबर के लिए एक सूचक की तुलना करते हैं तो एक और अपवाद है। जब एक सूचक दूसरे के बराबर होता है, तो आप जानते हैं कि यह उसी वस्तु की ओर इशारा करता है। यह जो कुछ भी है। यदि आप मुझसे पूछें तो बुरी परीक्षा का प्रश्न। उबंटू लिनक्स 16.04 पर निर्भर करता है, एक परीक्षा प्रश्न के लिए 64-बिट संस्करण प्रोग्रामिंग वातावरण? वास्तव में ?


तकनीकी तौर पर, सरणियों नहीं हैं वास्तव में एक अपवाद के बाद से आप नहीं घोषित करते हैं arr[0], arr[1], आदि अलग से। आप arrएक पूरे के रूप में घोषित करते हैं, इसलिए व्यक्तिगत सरणी तत्वों का क्रम इस प्रश्न में वर्णित एक अलग मुद्दा है।
paxdiablo

1
संरचना तत्वों को क्रम में होने की गारंटी दी जाती है, जो गारंटी देता है कि memcpyकिसी संरचना के एक सन्निहित भाग की प्रतिलिपि बनाने के लिए उपयोग किया जा सकता है और उसमें सभी तत्वों को प्रभावित कर सकता है और किसी अन्य चीज को प्रभावित नहीं कर सकता है। मानक शब्दावली के बारे में मैला है कि किस प्रकार का सूचक अंकगणित संरचनाओं या malloc()आवंटित भंडारण के साथ किया जा सकता है । offsetofमैक्रो बल्कि बेकार है अगर एक सकता है एक साथ के रूप में एक struct के बाइट्स के साथ सूचक अंकगणित की एक ही तरह के लिए नहीं किया जाएगा char[], लेकिन मानक स्पष्ट रूप से कहते हैं कि यह नहीं है कि एक struct के बाइट्स रहे हैं (या के रूप में इस्तेमाल किया जा सकता) एक सरणी वस्तु।
सुपरकैट

-4

क्या एक उत्तेजक सवाल!

यहां तक ​​कि इस थ्रेड में प्रतिक्रियाओं और टिप्पणियों की सरसरी स्कैनिंग से यह पता चलेगा कि आपकी प्रतीत होने वाली सरल और सीधे आगे की क्वेरी कैसे भावनात्मक हो जाती है।

इसमें आश्चर्य नहीं होना चाहिए।

निस्संदेह, संकेत की अवधारणा और उपयोग के बारे में गलतफहमी सामान्य रूप से प्रोग्रामिंग में गंभीर विफलताओं का एक प्रमुख कारण है ।

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

अंतर्निहित सिद्धांतों की गहरी समझ विकसित करना, इसलिए प्रत्येक व्यक्ति के लिए प्रासंगिक होना चाहिए जो प्रोग्रामिंग में उत्कृष्टता की इच्छा रखता है - विशेष रूप से सिस्टम स्तर पर

मुझे लगता है कि यह वही है जो आपके शिक्षक को प्रदर्शित करने का मतलब है।

और सी की प्रकृति इस अन्वेषण के लिए एक सुविधाजनक वाहन बनाती है। विधानसभा की तुलना में कम स्पष्ट रूप से - हालांकि शायद अधिक आसानी से समझ में आता है - और अभी भी निष्पादन पर्यावरण के गहन अमूर्तता पर आधारित भाषाओं की तुलना में अधिक स्पष्ट रूप से।

प्रोग्रामर के इरादों के निर्धारक अनुवाद को सुविधाजनक बनाने के लिए बनाया गया है , जो निर्देश दे सकता है कि मशीनें एक प्रणाली स्तर की भाषा हैं। उच्च-स्तरीय के रूप में वर्गीकृत करते हुए, यह वास्तव में 'मध्यम' श्रेणी में आता है; लेकिन चूंकि ऐसा कोई भी मौजूद नहीं है, इसलिए 'प्रणाली' के पदनाम को पर्याप्त करना होगा।

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

C क्या बनाता है - और इसके कुछ व्युत्पन्न - काफी विशेष है, यह है कि यह अपने उपयोगकर्ताओं को पूर्ण नियंत्रण की अनुमति देता है - जब उनकी इच्छा होती है - उन पर संबंधित जिम्मेदारियों को लागू किए बिना जब वे नहीं करते हैं। फिर भी, यह कभी नहीं की तुलना में अधिक प्रदान करता है insulations की सबसे पतला से मशीन , किस समुचित उपयोग की मांग मांग समझ की अवधारणा के संकेत दिए गए

संक्षेप में, आपके प्रश्न का उत्तर बेहद सरल और संतोषजनक रूप से मीठा है - आपके संदेह की पुष्टि में। बशर्ते , कि इस कथन में प्रत्येक अवधारणा को अपेक्षित महत्व दिया जाए:

  • पॉइंटर्स की जांच, तुलना और हेरफेर करने के कार्य हमेशा और आवश्यक रूप से मान्य होते हैं, जबकि परिणाम से प्राप्त निष्कर्ष निहित मूल्यों की वैधता पर निर्भर करता है, और इस तरह की आवश्यकता नहीं है।

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

बेशक, भ्रम का हिस्सा एक संकेतक के सिद्धांत के भीतर अंतर्निहित पुनरावृत्ति के प्रभाव से उत्पन्न होता है - और पता से सामग्री को अलग करने में उत्पन्न चुनौतियां।

आपने बहुत सही ढंग से सर्मिपत किया है,

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

और कई योगदानकर्ताओं ने पुष्टि की है: संकेत केवल संख्याएं हैं। कभी-कभी कुछ जटिल संख्याओं के करीब होते हैं, लेकिन फिर भी संख्याओं से अधिक नहीं।

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

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

उस छोर की ओर, हमें सबसे पहले सराहना की जरूरत है ठीक कि एक पॉइंटर क्या है

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

जैसा कि कई ने बताया है: पॉइंटर शब्द केवल एक विशेष नाम है जो कि बस एक है इंडेक्स के, और इस प्रकार यह किसी भी अन्य संख्या से अधिक नहीं है।

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

तकनीकी रूप से, जैसा कि आपने उल्लेख किया है, संकेत अधिक सटीक पते हैं ; एक स्पष्ट अंतर्दृष्टि जो स्वाभाविक रूप से घरों के 'पतों', या एक सड़क पर भूखंडों के साथ उन्हें सहसंबद्ध करने के पुरस्कृत सादृश्य का परिचय देती है।

  • में फ्लैट मेमोरी मॉडल में: संपूर्ण सिस्टम मेमोरी एक एकल, रैखिक अनुक्रम में आयोजित की जाती है: शहर के सभी घर एक ही सड़क पर स्थित हैं, और प्रत्येक घर की विशिष्ट पहचान अकेले इसकी संख्या से है। प्रसन्नतापूर्वक सरल।

  • में खंडित योजनाओं: गिने सड़कों का एक सौपानिक संगठन गिने घरों ताकि समग्र पतों के लिए आवश्यक हैं की है कि इसके बाद के संस्करण शुरू की है।

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

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

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

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

पॉइंटर्स सिर्फ एड्रेस हैं, और एड्रेस सिर्फ नंबर हैं।

निम्नलिखित के उत्पादन को सत्यापित करें:

void foo( void *p ) {
   printf(“%p\t%zu\t%d\n”, p, (size_t)p, p == (size_t)p);
}

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

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

  • हम केवल स्पष्टता के लिए result स्पष्ट रूप से गणना कर रहे हैं , और इसे संकलित करने के लिए मजबूर करने के लिए छपाई कर रहे हैं कि क्या अन्यथा अनावश्यक, मृत कोड होगा।
void foo( size_t *a, size_t *b ) {
   size_t result;
   result = (size_t)a;
   printf(“%zu\n”, result);
   result = a == b;
   printf(“%zu\n”, result);
   result = a < b;
   printf(“%zu\n”, result);
   result = a - b;
   printf(“%zu\n”, result);
}

बेशक, यह कार्यक्रम बीमार है, जब परीक्षण के बिंदु पर या तो एक या बी अपरिभाषित है (पढ़ें: ठीक से शुरू नहीं किया गया है ), लेकिन यह हमारी चर्चा के इस हिस्से के लिए पूरी तरह अप्रासंगिक है। ये स्निपेट भी निम्नलिखित बयानों के रूप में, कर रहे हैं गारंटी - द्वारा 'मानक' - करने के लिए संकलन और चलाने दोषरहित, के होते हुए भी में किसी भी शामिल सूचक की -validity।

समस्याएँ तभी उत्पन्न होती हैं जब एक अमान्य सूचक को निष्क्रिय कर दिया जाता है । जब हम फ्रैंक को अमान्य, गैर-मौजूद पते पर लेने या देने के लिए कहते हैं।

किसी भी मनमानी सूचक को देखते हुए:

int *p;

हालांकि इस कथन को संकलित और चलाना चाहिए:

printf(“%p”, p);

... जैसा कि यह होना चाहिए:

size_t foo( int *p ) { return (size_t)p; }

... दो के बाद, विपरीत, अभी भी आसानी से संकलन होगा, लेकिन असफल निष्पादन में जब तक सूचक है वैध - जिसके द्वारा हम यहां केवल मतलब यह है कि एक पता जिस पर वर्तमान आवेदन पहुंच प्रदान की गई संदर्भ देता है :

printf(“%p”, *p);
size_t foo( int *p ) { return *p; }

परिवर्तन कितना सूक्ष्म? जो - सूचक के मूल्य के बीच का अंतर में अंतर झूठ है कि संख्या में घर से: पता, और सामग्री के मूल्य। कोई समस्या तब तक नहीं उठती जब तक कि पॉइंटर को डिफाइन नहीं किया जाता ; जब तक उस पते तक पहुंचने का प्रयास नहीं किया जाता है, जिससे वह लिंक करता है। सड़क के खिंचाव से परे पैकेज देने या लेने की कोशिश में ...

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

int* validate( int *p, int *head, int *tail ) { 
    return p >= head && p <= tail ? p : NULL; 
}

संबंधपरक तुलना और अंकगणित समतुल्यता के परीक्षण के लिए समान उपयोगिता प्रदान करते हैं, और समान रूप से मान्य हैं - सिद्धांत रूप में। हालाँकि , इस तरह की गणना के परिणाम क्या संकेत देते हैं , यह पूरी तरह से एक अलग मामला है - और ठीक आपके द्वारा शामिल किए गए उद्धरणों द्वारा संबोधित किया गया मुद्दा।

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

अलग-अलग ब्लॉकों या सरणियों को संदर्भित करने वाले बिंदुओं पर समान जिमनास्टिक प्रदर्शन करना इस तरह की अंतर्निहित , और स्पष्ट उपयोगिता की पेशकश नहीं करता है । एक पल में जो भी संबंध मौजूद होता है, वह अधिक से अधिक एक वास्तविक स्थान द्वारा अमान्य हो सकता है, जो कि परिवर्तन की अत्यधिक संभावना है, यहां तक ​​कि उलटा भी हो सकता है। इस तरह के मामलों में कंपाइलर पिछली स्थिति में जो आत्मविश्वास था उसे स्थापित करने के लिए आवश्यक जानकारी प्राप्त करने में असमर्थ है।

आप , हालांकि, प्रोग्रामर के रूप में,ऐसा ज्ञान हो सकता है! और कुछ उदाहरणों में इसका फायदा उठाने के लिए बाध्य किया जाता है।

वहाँ हैं , इसलिए, जिन परिस्थितियों में भी पूरी तरह से वैध और पूरी तरह से उचित।

वास्तव में, यह वही है जो mallocखुद को आंतरिक रूप से करना पड़ता है जब समय आवर्ती ब्लॉकों को विलय करने की कोशिश करता है - विशाल बहुमत पर। ऑपरेटिंग सिस्टम आवंटनकर्ता के लिए भी यही सच है, जैसे कि पीछे sbrk; यदि अधिक स्पष्ट रूप से , अक्सर , अधिक विषम संस्थाओं पर, अधिक गंभीर रूप से - और प्रासंगिक उन प्लेटफार्मों पर भी जहां यह mallocनहीं हो सकता है। और C में से कितने नहीं लिखे गए हैं?

किसी कार्रवाई की वैधता, सुरक्षा और सफलता अनिवार्य रूप से अंतर्दृष्टि के स्तर का परिणाम है, जिस पर यह आधार और लागू होता है।

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

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

वास्तव में, यह बस कहता है: 'इस बिंदु से परे, काउबॉय : आप अपने दम पर हैं ...'

आपका प्रोफेसर आपसे बारीक बारीकियों को प्रदर्शित करना चाहता है।

ध्यान दें कि उन्होंने अपना उदाहरण प्रस्तुत करने में कितनी सावधानी बरती है; और यह अभी भी कितना भंगुर है। में , का पता लेकरa

p[0].p0 = &a;

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

इस लाइन को बदलते हुए:

char a = 0;

इसके लिए:

char a = 1;  // or ANY other value than 0

कार्यक्रम के व्यवहार को अपरिभाषित होने का कारण बनता है । कम से कम, पहला उत्तर अब 1 होगा; लेकिन समस्या कहीं अधिक भयावह है।

अब कोड आपदा को आमंत्रित कर रहा है।

हालांकि अभी भी पूरी तरह से वैध है और मानक के अनुरूप भी है , लेकिन अब यह बीमार है और संकलन के लिए सुनिश्चित है, विभिन्न आधारों पर निष्पादन में विफल हो सकता है। अभी के लिए देखते हैं कई समस्याओं - कोई भी जिनमें से संकलक है सक्षम करने के लिए स्वीकार करते हैं।

strcpyके पते पर शुरू होगा a, और इस से आगे बढ़ने के लिए उपभोग - और हस्तांतरण - बाइट के बाद बाइट, जब तक कि यह एक नल का सामना नहीं करता।

p1सूचक बिल्कुल के एक ब्लॉक के लिए शुरू कर दिया गया है 10 बाइट्स।

  • अगर aकिसी ब्लॉक के अंत में रखा जाता है और इस प्रक्रिया का कोई उपयोग नहीं होता है, तो अगले पढ़ें - p0 [1] की - एक सेगफॉल्ट को हटा देगा। यह परिदृश्य है संभावना नहीं x86 आर्किटेक्चर पर है, लेकिन संभव।

  • यदि का पता परे क्षेत्र a है सुलभ, कोई पठन त्रुटि हो जाएगा, लेकिन कार्यक्रम अभी भी दुर्भाग्य से सहेजा नहीं गया है।

  • यदि एक शून्य बाइट के पते पर शुरू होने वाले दस के भीतर होता है a, तो यह अभी भी जीवित रह सकता है , इसके लिए strcpyबंद हो जाएगा और कम से कम हम एक लेखन उल्लंघन नहीं भुगतेंगे।

  • यदि यह एमिस पढ़ने के लिए दोषपूर्ण नहीं है , लेकिन 10 के इस अवधि में कोई शून्य बाइट नहीं होता है, strcpyतो जारी रहेगा और द्वारा आवंटित ब्लॉक से परे लिखने का प्रयास करेगा malloc

    • यदि यह क्षेत्र प्रक्रिया के स्वामित्व में नहीं है, तो सेगफॉल्ट को तुरंत ट्रिगर किया जाना चाहिए।

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

यही कारण है कि पॉइंटर संबंधित बगों को ट्रैक करना इतना कठिन हो सकता है । कल्पना करें कि इन पंक्तियों को गहन रूप से संबंधित कोड की हजारों लाइनों के भीतर गहरे दफन किया गया है, जिसे किसी और ने लिखा है, और आपको निर्देश दिया गया है।

फिर भी , कार्यक्रम को अभी भी संकलित करना चाहिए , क्योंकि यह पूरी तरह से वैध और मानक अनुरूप सीबना हुआ है

इस प्रकार की त्रुटियां, कोई मानक और कोई संकलक अनैच्छिक रक्षा नहीं कर सकते हैं। मुझे लगता है कि वास्तव में वे आपको पढ़ाने का इरादा कर रहे हैं।

पैरानॉयड लोग लगातार इन समस्याग्रस्त संभावनाओं के निपटान के लिए सी की प्रकृति को बदलना चाहते हैं और इसलिए हमें खुद से बचाते हैं; लेकिन यह अपमानजनक है । यह वह जिम्मेदारी है जिसे हम स्वीकार करने के लिए बाध्य होते हैं जब हम सत्ता का पीछा करने के लिए चुनते हैं और स्वतंत्रता प्राप्त करते हैं जो मशीन का अधिक प्रत्यक्ष और व्यापक नियंत्रण हमें प्रदान करता है। प्रदर्शन में पूर्णता के प्रवर्तक और अनुयायी कभी भी कुछ कम स्वीकार नहीं करेंगे।

पोर्टेबिलिटी और सामान्यता जो इसका प्रतिनिधित्व करती है, एक मौलिक रूप से अलग विचार है और सभी जो मानक को संबोधित करता है:

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

यही कारण है कि इसे भाषा की परिभाषा और तकनीकी विनिर्देश से अलग रखना पूरी तरह से उचित है। कई क्या के विपरीत विश्वास करने लगते हैं व्यापकता है विरोधात्मक को असाधारण और अनुकरणीय

समाप्त करने के लिए:

  • बिंदुओं की जांच करना और हेरफेर करना हमेशा ही मान्य और अक्सर फलदायी होता है । परिणामों की व्याख्या, अर्थपूर्ण हो सकती है या नहीं भी हो सकती है, लेकिन जब तक कि पॉइंटर को स्थगित नहीं किया जाता है , तब तक विपत्ति को कभी भी आमंत्रित नहीं किया जाता है ; जब तक इससे जुड़े पते तक पहुंचने का प्रयास नहीं किया जाता ।

क्या यह सच नहीं था, प्रोग्रामिंग जैसा कि हम जानते हैं - और इसे प्यार करते हैं - संभव नहीं था।


3
यह उत्तर दुर्भाग्य से अमान्य है। आप अपरिभाषित व्यवहार के बारे में कुछ भी कारण नहीं बता सकते। मशीन स्तर पर तुलना की जरूरत नहीं है।
अंती हापला

6
घी, वास्तव में नहीं। यदि आप C11 अनुलग्नक J और 6.5.8 को देखें, तो तुलना का कार्य UB है। डेरेफ्रेंसिंग एक अलग मुद्दा है।
पैक्सडिआब्लो

6
नहीं, UB अभी भी हानिकारक हो सकता है इससे पहले कि एक पॉइंटर को डिरेल किया जाए। एक संकलक यूबी के साथ एक फ़ंक्शन को पूरी तरह से एक एकल एनओपी में अनुकूलित करने के लिए स्वतंत्र है, भले ही यह स्पष्ट रूप से दृश्यमान व्यवहार को बदलता है।
नानोफैरड

2
@Ghii, अनुलग्नक J (थोड़ा मैंने उल्लेख किया) अपरिभाषित व्यवहार की सूची है , इसलिए मुझे यकीन नहीं है कि यह कैसे आपके तर्क का समर्थन करता है :-) 6.5.8 स्पष्ट रूप से यूबी के रूप में तुलना कहते हैं। सुपरकैट के लिए आपकी टिप्पणी के लिए, जब आप एक पॉइंटर प्रिंट करते हैं तो कोई तुलना नहीं होती है ताकि आप शायद सही हों कि यह दुर्घटनाग्रस्त न हो। लेकिन ओपी के बारे में ऐसा नहीं था। 3.4.3यह भी एक खंड है जिसे आपको देखना चाहिए: यह व्यवहार के रूप में यूबी को परिभाषित करता है "जिसके लिए यह अंतर्राष्ट्रीय मानक कोई आवश्यकता नहीं लगाता है"।
पैक्सडीब्लो

3
@GhiiVelte, आप उन चीजों को बताते रहते हैं जो सीधे तौर पर हैं गलत हैं, इसके बावजूद आपको इंगित किया गया है। हां, आपने जो स्निपेट पोस्ट किया है वह अवश्य संकलित करना चाहिए लेकिन आपका विवाद यह है कि यह बिना किसी अड़चन के चल रहा है। मेरा सुझाव है कि आप वास्तव में मानक पढ़ते हैं , विशेष रूप से (इस मामले में) C11 6.5.6/9, यह ध्यान में रखते हुए कि "शब्द" एक आवश्यकता को इंगित करता है "जब दो बिंदुओं को घटाया जाता है, तो दोनों एक ही सरणी वस्तु के तत्वों को इंगित करेंगे, या एक आखिरी होगा सरणी वस्तु का तत्व "।
paxdiablo

-5

पॉइंटर्स केवल पूर्णांक हैं, जैसे कंप्यूटर में सब कुछ। यदि आप पूरी तरह से उनकी तुलना कर सकते हैं <और >दुर्घटना के लिए एक कार्यक्रम के कारण के बिना और उत्पादन का परिणाम है। उस ने कहा, मानक गारंटी नहीं देता है कि उन परिणामों का सरणी तुलना के बाहर कोई अर्थ है

स्टैक आबंटित चर के आपके उदाहरण में, कंपाइलर उन चरों को रजिस्टरों या स्टैक मेमोरी पतों को आवंटित करने के लिए स्वतंत्र है, और किसी भी क्रम में इसे चुनते हैं। तुलना <और >इसलिए कंपाइलर या आर्किटेक्चर के अनुरूप नहीं होगा। हालाँकि, ==और !=इतना प्रतिबंधित नहीं हैं, सूचक समानता की तुलना करना एक वैध और उपयोगी ऑपरेशन है।


2
स्टैक शब्द सी 11 मानक में बिल्कुल शून्य बार दिखाई देता है । और अपरिभाषित व्यवहार का मतलब कुछ भी हो सकता है (प्रोग्राम क्रैश सहित)।
पैक्सिडाब्लो

1
@paxdiablo मैंने ऐसा कहा था?
निकेलप्रो

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

1
@nickelpro: यदि कोई ऐसा कोड लिखना चाहता है जो gcc और क्लैंग में ऑप्टिमाइज़र के साथ संगत हो, तो बहुत सारे मूर्ख हूप के माध्यम से कूदना आवश्यक है। दोनों ऑप्टिमाइज़र आक्रामक तरीके से इनवॉइस आकर्षित करने के अवसरों की तलाश करेंगे, जब भी पॉइंटर्स द्वारा एक्सेस किया जाएगा, जब भी कोई मानक हो तो उन्हें जस्टिफाई करने के लिए घुमाया जा सकता है (और कभी-कभी ऐसा नहीं होने पर भी)। यह देखते हुए int x[10],y[10],*p;, यदि कोड मूल्यांकन करता है y[0], तो अंतरिम में संशोधन किए बिना मूल्यांकन करता है p>(x+5)और लिखता है , और अंत में फिर से मूल्यांकन करता है , ...*ppy[0]
19

1
निकलप्रो, असहमत होने के लिए सहमत होने के लिए सहमत हैं लेकिन आपका जवाब अभी भी मौलिक रूप से गलत है। मैं आपके दृष्टिकोण की तुलना उन लोगों से करता हूं जो (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')इसके बजाय उपयोग करते हैं isalpha()क्योंकि किन कार्यान्वयनों से उन पात्रों को रोक दिया जाएगा? लब्बोलुआब यह है कि, भले ही कोई कार्यान्वयन आपको पता नहीं है कि एक समस्या है, आपको पोर्टेबिलिटी होने पर यथासंभव मानक के लिए कोडिंग करना चाहिए। मैं हालांकि "मानक मावेन" लेबल की सराहना करता हूं, इसके लिए धन्यवाद। मैं अपने CV पर डाल सकता हूँ :-)
paxdiablo
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.