सूचक अनुक्रमण


11

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

float *a = malloc(size);
a--;

यह, वह कहते हैं, प्रभावी रूप से आपको एक संकेतक देगा जो कि 1 के साथ शुरू होने वाला एक सूचकांक है, जो तब के साथ मुक्त होगा:

free(a + 1);

जहाँ तक मुझे जानकारी है, हालाँकि, यह C मानक द्वारा अपरिभाषित व्यवहार है। यह जाहिरा तौर पर एचपीसी समुदाय के भीतर एक बहुत ही प्रतिष्ठित पुस्तक है, इसलिए मैं केवल यह कहना नहीं चाहता कि वह क्या कह रहा है, लेकिन बस आवंटित सीमा के बाहर एक संकेतक को कम करने के लिए मुझे अत्यधिक संदेह है। क्या यह "अनुमति" सी में व्यवहार है? मैंने gcc और icc दोनों का उपयोग करके इसका परीक्षण किया है, और उन दोनों परिणामों से यह प्रतीत होता है कि मैं कुछ भी नहीं होने की चिंता कर रहा हूं, लेकिन मैं बिल्कुल सकारात्मक होना चाहता हूं।


3
आप किस C मानक का उल्लेख करते हैं? मैं पूछता हूं कि मेरे स्मरण के अनुसार, "न्यूमेरिकल रेसिपीज़ इन सी" 1990 के दशक में K & R और शायद ANSI C के प्राचीन समय में प्रकाशित हुआ था
gnat

2
संबंधित SO प्रश्न: stackoverflow.com/questions/10473573/…
dan04

3
"मैंने gcc और icc दोनों का उपयोग करके इसका परीक्षण किया है, और उन दोनों परिणामों से यह प्रतीत होता है कि मैं कुछ भी नहीं होने की चिंता कर रहा हूं, लेकिन मैं पूरी तरह सकारात्मक होना चाहता हूं।" यह कभी न मानें कि क्योंकि आपका कंपाइलर इसे अनुमति देता है, C भाषा इसे अनुमति देती है। जब तक, निश्चित रूप से, आप भविष्य में अपने कोड को तोड़ने के साथ ठीक हैं।
डोवाल

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

1
एक तरफ के रूप में, यहाँ हम शून्य से सूचकांक क्यों हैं: cs.utexas.edu/~EWD/ewd08xx/EWD831.PDF
रसेल बोरोगोव

जवाबों:


16

आप सही हैं कि जैसे कोड

float a = malloc(size);
a--;

ANSI C मानक, खंड 3.3.6 के अनुसार अपरिभाषित व्यवहार करता है:

जब तक कि पॉइंटर ऑपरेंड और समान एरे ऑब्जेक्ट के एक सदस्य या परिणाम ऑब्जेक्ट के अंतिम सदस्य के पिछले एक बिंदु तक, व्यवहार अपरिभाषित नहीं होता है

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

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

बस इस बात से अवगत रहें कि रनटाइम वातावरण बदलने पर या कोड को अलग वातावरण में पोर्ट करने पर कोड टूट सकता है।


4
वास्तव में, यह एक बहु-बैंक वास्तुकला पर संभव है कि मॉलॉक आपको एक बैंक में 0 वां पता दे सकता है और इसे घटाकर एक के लिए एक अंडरफ्लो के साथ सीपीयू जाल पैदा कर सकता है।
वैलेटी

1
मैं असहमत हूं कि वह "भाग्यशाली" है। मुझे लगता है कि यह बहुत बेहतर होगा यदि कंपाइलर्स कोड उत्सर्जित करते हैं जो कि जब भी आप अपरिभाषित व्यवहार का आह्वान करते हैं तो तुरंत दुर्घटनाग्रस्त हो जाता है।
डेविड कॉनराड

4
@DavidConrad: तब C आपके लिए भाषा नहीं है। सी में अपरिभाषित व्यवहार के अधिकांश का पता आसानी से नहीं लगाया जा सकता है या केवल एक गंभीर प्रदर्शन हिट के साथ किया जा सकता है।
बार्ट वैन इनगेन शेनौ जूल

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

@BartvanIngenSchenau आपके द्वारा 'गंभीर प्रदर्शन हिट' के अर्थ के आधार पर C (उदाहरण के लिए clang + klee) के साथ-साथ sanatizers (asan, tsan, ubsan, valgrind) के लिए प्रतीकात्मक निष्पादन है, जो डिबगिंग के लिए बहुत उपयोगी होते हैं।
मैकीज पीचोटका

10

आधिकारिक तौर पर, सरणी के बाहर एक पॉइंटर पॉइंट के लिए यह अपरिभाषित व्यवहार है (एक छोर को छोड़कर), भले ही इसे कभी भी स्थगित नहीं किया गया हो

व्यवहार में, यदि आपके प्रोसेसर में एक फ्लैट मेमोरी मॉडल है (जैसा कि x86-16 जैसे अजीब लोगों के विपरीत है ), और यदि कंपाइलर आपको रनटाइम त्रुटि या गलत अनुकूलन नहीं देता है यदि आप एक अवैध सूचक बनाते हैं, तो कोड काम करेगा बस ठीक।


1
यह समझ आता है। दुर्भाग्य से, यह दो बहुत है अगर मेरी पसंद के लिए है।
भेड़ियापाक

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

3

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

इसे अनदेखा करते हुए, आप 1, या 2, या 1980 को घटा सकते हैं। उदाहरण के लिए यदि मेरे पास 1980 से 2013 के वर्षों के वित्तीय आंकड़े हैं, तो मैं 1980 को घटा सकता हूं। अब अगर हम फ्लोट * a = malloc (आकार) लेते हैं; निश्चित रूप से कुछ बड़े स्थिर k हैं जैसे कि - k एक अशक्त सूचक है। उस मामले में, हम वास्तव में कुछ गलत होने की उम्मीद करते हैं।

अब एक बड़ी संरचना लें, आकार में मेगाबाइट कहें। दो पुर्जों की ओर इशारा करते हुए एक पॉइंटर पी आवंटित करें। p - 1 एक अशक्त सूचक हो सकता है। p - 1 चारों ओर लपेट सकता है (यदि एक संरचना मेगाबाइट है, और मालॉक ब्लॉक पता स्थान की शुरुआत से 900 KB है)। तो यह संकलक के किसी भी द्वेष के बिना हो सकता है कि पी - 1> पी। चीजें दिलचस्प हो सकती हैं।


1

... बस आवंटित सीमा के बाहर एक सूचक decrementing मेरे लिए बहुत ही डरावना लगता है। क्या यह "अनुमति" सी में व्यवहार है?

अनुमति है? हाँ। अच्छा विचार? आमतौर पर नहीं।

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

double *p = (double *)0xdeadbeef;
--p;  // p == 0xdeadbee7, assuming sizeof(double) == 8.
double d = p[0];

Arrays वास्तव में C में एक चीज नहीं है; वे सिर्फ स्मृति की सन्निहित सीमाओं की ओर संकेत करते हैं जो सरणियों की तरह व्यवहार करते हैं। []ऑपरेटर सूचक अंकगणित कर रहे हैं और अपसंदर्भन, तो के लिए एक आशुलिपि है a[x]साधन वास्तव में *(a + x)

ऐसे कुछ मैं / हे की एक जोड़ी होने डिवाइस के रूप में ऊपर करने के लिए वैध कारण हैं, doubleमें मैप किया रों 0xdeadbee7और 0xdeadbeef। बहुत कम कार्यक्रमों को ऐसा करने की आवश्यकता होगी।

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

यदि आपको C में 1-आधारित सरणियों की आवश्यकता है, तो आप इसे एक अतिरिक्त तत्व को आवंटित करने की कीमत पर सुरक्षित रूप से कर सकते हैं जिसका उपयोग कभी नहीं किया जाएगा:

double *array_create(size_t size) {
    // Wasting one element, so don't allow it to be full-sized
    assert(size < SIZE_MAX);
    return malloc((size+1) * sizeof(double));
}

inline double array_index(double *array, size_t index) {
    assert(array != NULL);
    assert(index >= 1);  // This is a 1-based array
    return array[index];
}

ध्यान दें कि यह ऊपरी सीमा से अधिक की रक्षा करने के लिए कुछ भी नहीं करता है, लेकिन यह काफी आसान है।


परिशिष्ट:

C99 ड्राफ्ट से कुछ अध्याय और आयतें (क्षमा करें, यह सब मेरे साथ लिंक हो सकता है):

§6.5.2.1.1 का कहना है कि सबस्क्रिप्ट ऑपरेटर के साथ प्रयोग किया जाने वाला दूसरा ("अन्य") पूर्णांक प्रकार है। -1एक पूर्णांक है, और जो p[-1]वैध बनाता है और इसलिए सूचक को &(p[-1])वैध बनाता है। इसका मतलब यह नहीं है कि उस स्थान पर मेमोरी एक्सेस करना परिभाषित व्यवहार का उत्पादन करेगा, लेकिन सूचक अभी भी एक वैध सूचक है।

§6.5.2.2 का कहना है कि सरणी सबस्क्रिप्ट ऑपरेटर सूचक के लिए तत्व संख्या को जोड़ने के बराबर का मूल्यांकन करता है, इसलिए p[-1]इसके बराबर है *(p + (-1))। फिर भी मान्य है, लेकिन वांछनीय व्यवहार उत्पन्न नहीं कर सकता है।

§6.5.6.8 कहते हैं (जोर मेरा):

जब किसी सूचक का पूर्णांक प्रकार एक सूचक से जोड़ा या घटाया जाता है, तो परिणाम में सूचक ऑपरेटर का प्रकार होता है।

... यदि अभिव्यक्ति एक सरणी वस्तु के भाव तत्व Pको इंगित करता है i, तो अभिव्यक्तियाँ (P)+N(समतुल्य N+(P)) , और (P)-N (जहां Nमूल्य है n) क्रमशः, वस्तु वस्तु के i+n-th और i−n-th तत्वों को इंगित करता है , बशर्ते कि वे मौजूद हों

इसका मतलब है कि सूचक अंकगणित के परिणामों को एक सरणी में एक तत्व पर इंगित करना है। यह नहीं कहता है कि अंकगणित को एक ही बार में किया जाना है। इसलिए:

double a[20];

// This points to element 9 of a; behavior is defined.
double d = a[-1 + 10];

double *p = a - 1;  // This is just a pointer.  No dereferencing.

double e = p[0];   // Does not point at any element of a; behavior is undefined.
double f = p[1];   // Points at element 0 of a; behavior is defined.

क्या मैं इस तरह से काम करने की सलाह देता हूं? मैं नहीं, और मेरा जवाब क्यों बताता है।


8
-1 'अनुमत' की एक परिभाषा जिसमें कोड शामिल है सी मानक घोषणाओं को अपरिभाषित परिणाम उत्पन्न करने के लिए उपयोगी नहीं है।
पीट किरकम

दूसरों ने बताया कि यह अपरिभाषित व्यवहार है, इसलिए आपको यह नहीं कहना चाहिए कि यह "अनुमति" है। हालांकि, एक अतिरिक्त अप्रयुक्त तत्व 0 आवंटित करने का सुझाव अच्छा है।
200_सेफ

यह वास्तव में सही नहीं है, कृपया कम से कम ध्यान दें कि यह सी मानक द्वारा निषिद्ध है।
वैलेटी

@PeteKirkham: मैं असहमत हूं। मेरे उत्तर के लिए परिशिष्ट देखें।
ब्लरफुल

4
ISOB11 मानक के @Blfl 6.5.6 एक पॉइंटर में पूर्णांक जोड़ने के मामले में कहता है: "यदि पॉइंटर ऑपरेटर और परिणाम दोनों समान सरणी ऑब्जेक्ट के तत्वों को इंगित करते हैं, या सरणी ऑब्जेक्ट के अंतिम तत्व से एक है। मूल्यांकन एक अतिप्रवाह का उत्पादन नहीं करेगा, अन्यथा, व्यवहार अपरिभाषित है। "
०६ पर वैलिट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.