सी पॉइंटर को बिटवाइज और ऑपरेटर के साथ ऐरे ऐलान करने के लिए


9

मैं निम्नलिखित कोड समझना चाहता हूं:

//...
#define _C 0x20
extern const char *_ctype_;
//...
__only_inline int iscntrl(int _c)
{
    return (_c == -1 ? 0 : ((_ctype_ + 1)[(unsigned char)_c] & _C));
}

यह फ़ाइल ctype.h से ओबन्सबर्ड ऑपरेटिंग सिस्टम सोर्स कोड से उत्पन्न होता है । यह फ़ंक्शन जाँचता है कि क्या एक चार्ली नियंत्रण वर्ण या एससीआई रेंज के अंदर एक मुद्रण योग्य पत्र है। यह मेरे विचार की वर्तमान श्रृंखला है:

  1. iscntrl ('a') कहलाता है और 'a' इसे पूर्णांक मान में परिवर्तित करता है
  2. पहले जांचें कि क्या _c -1 है तो 0 और लौटाएं ...
  3. 1 से अपरिभाषित सूचक बिंदुओं को बढ़ाएँ
  4. इस एड्रेस को एक पॉइंटर के रूप में लंबाई (अहस्ताक्षरित चार) ((int) 'a') घोषित करें
  5. बिटकॉइन और ऑपरेटर को _C (0x20) और सरणी (???) पर लागू करें

किसी तरह, अजीब तरह से, यह काम करता है और हर बार जब 0 दिया गया चार _ _ प्रिंट करने योग्य चरित्र नहीं होता है। अन्यथा जब यह मुद्रण योग्य होता है तो फ़ंक्शन केवल पूर्णांक मान लौटाता है जो किसी विशेष हित का नहीं है। मेरी समझ की समस्या चरण 3, 4 (थोड़ा) और 5 में है।

हर प्रकार की सहायता के लिए आपका धन्यवाद।


1
_ctype_अनिवार्य रूप से बिटमास्क की एक सरणी है। यह ब्याज के चरित्र द्वारा अनुक्रमित किया जा रहा है। तो _ctype_['A']"अल्फ़ा" और "अपरकेस" के _ctype_['a']समान बिट्स होंगे , जिनमें "अल्फ़ा" और "लोअरकेस" के अनुरूप बिट्स _ctype_['1']होंगे , जिनमें "अंक" के समान थोड़ा सा होगा, आदि ऐसा लगता 0x20है कि "नियंत्रण" के अनुरूप बिट है। । लेकिन किसी कारण से _ctype_सरणी 1 से ऑफसेट है, इसलिए बिट्स 'a'वास्तव में हैं _ctype_['a'+1]। (यह EOFअतिरिक्त परीक्षण के बिना भी काम करने देना था ।)
स्टीव समिट

कलाकारों के (unsigned char)हस्ताक्षर और नकारात्मक होने की संभावना का ध्यान रखना है।
स्टीव समिट

जवाबों:


3

_ctype_प्रतीक तालिका का एक प्रतिबंधित आंतरिक संस्करण प्रतीत होता है और मैं अनुमान लगा रहा हूं + 1कि वे इसे बचाने के सूचकांक 0को परेशान नहीं करते हैं क्योंकि यह मुद्रण योग्य नहीं है। या संभवतः वे 0-अनुक्रमित के बजाय 1-अनुक्रमित तालिका का उपयोग कर रहे हैं जैसा कि सी में कस्टम है।

C मानक सभी ctype.h फ़ंक्शन के लिए इसे निर्धारित करता है:

सभी मामलों में तर्क एक है int, जिसका मूल्य unsigned charमैक्रो के रूप में एक के रूप में प्रतिनिधित्व करने योग्य होगा या होगाEOF

चरण दर चरण कोड के माध्यम से जाना:

  • int iscntrl(int _c)intप्रकार के वास्तव में चरित्र है, लेकिन सभी ctype.h कार्यों संभाल के लिए आवश्यक हैं EOF, ताकि वे किया जाना चाहिएint
  • के खिलाफ चेक -1एक चेक के खिलाफ है EOF, क्योंकि इसका मूल्य है-1
  • _ctype+1 एक सरणी वस्तु का पता प्राप्त करने के लिए सूचक अंकगणित है।
  • [(unsigned char)_c]बस उस ऐरे की एक ऐक्सेस एक्सेस है, जहाँ के रूप में प्रतिनिधित्व करने योग्य पैरामीटर की मानक आवश्यकता को लागू करने के लिए कास्ट है unsigned char। ध्यान दें कि charवास्तव में एक नकारात्मक मूल्य पकड़ सकता है, इसलिए यह रक्षात्मक प्रोग्रामिंग है। []सरणी पहुंच का परिणाम उनके आंतरिक प्रतीक तालिका से एक एकल वर्ण है।
  • &मास्किंग प्रतीक मेज से पात्रों में से एक निश्चित समूह पाने के लिए नहीं है। स्पष्ट रूप से बिट 5 सेट (मास्क 0x20) वाले सभी वर्ण नियंत्रण वर्ण हैं। तालिका देखे बिना इसका कोई अर्थ नहीं है।
  • बिट 5 सेट के साथ कुछ भी 0x20 के साथ नकाबपोश मान लौटाएगा, जो एक गैर-शून्य मान है। यह बूलियन सच के मामले में गैर-शून्य लौटने वाले फ़ंक्शन की आवश्यकता को पूरा करता है।

यह सही नहीं है कि कलाकार मानक आवश्यकता को दर्शाता है जो मान के रूप में प्रतिनिधित्व करने योग्य है unsigned char। मानक की आवश्यकता है कि पहले से ही मूल्य * के रूप में प्रतिनिधित्व योग्य हो unsigned char, या समान EOF, जब दिनचर्या कहा जाता है। कलाकार केवल "रक्षात्मक" प्रोग्रामिंग के रूप में कार्य करता है: एक प्रोग्रामर की त्रुटि को ठीक करता है जो एक हस्ताक्षरित char(या क signed char) पास करता है unsigned charजब ctype.hमैक्रो का उपयोग करते समय एक मान पारित करने के लिए उन पर था । यह ध्यान दिया जाना चाहिए कि यह त्रुटि को सही नहीं कर सकता है जब charimplementation1 का उपयोग करने वाले कार्यान्वयन में cannot1 का मान पारित किया जाता है EOF
एरिक पोस्टपिसिल

यह भी की एक व्याख्या प्रदान करता है + 1 । यदि मैक्रो में पहले यह रक्षात्मक समायोजन नहीं था, तो इसे केवल ((_ctype_+1)[_c] & _C)इस प्रकार लागू किया जा सकता है , इस प्रकार पूर्व समायोजन मूल्यों के साथ अनुक्रमित तालिका -1 से 255 हो जाती है। इसलिए पहली प्रविष्टि को छोड़ नहीं दिया गया और एक उद्देश्य पूरा नहीं किया। जब किसी ने बाद में रक्षात्मक कलाकारों को जोड़ा, not1 का EOFमान उस कलाकारों के साथ काम नहीं करेगा, इसलिए उन्होंने विशेष रूप से इलाज करने के लिए सशर्त ऑपरेटर को जोड़ा।
एरिक पोस्टपिसिल

3

_ctype_257 बाइट्स के वैश्विक सरणी के लिए एक संकेतक है। मुझे नहीं पता कि इसके लिए क्या _ctype_[0]उपयोग किया जाता है। _ctype_[1]के माध्यम _ctype_[256]_से वर्णों की वर्ण श्रेणियों का प्रतिनिधित्व करते हैं 0,…, क्रमशः 255: _ctype_[c + 1]चरित्र की श्रेणी का प्रतिनिधित्व करता है c। यह वही बात है जो यह कहती है कि यह _ctype_ + 1256 वर्णों की एक सरणी की (_ctype_ + 1)[c]ओर इंगित करता है जहां वर्ण की श्रेणीबद्धता का प्रतिनिधित्व करता है c

(_ctype_ + 1)[(unsigned char)_c]एक घोषणा नहीं है। यह सरणी सबस्क्रिप्ट ऑपरेटर का उपयोग करके एक अभिव्यक्ति है। यह (unsigned char)_cउस सरणी की स्थिति तक पहुँच रहा है जो शुरू होता है (_ctype_ + 1)

कोड डालती है _c से intकरने के लिए unsigned charसख्ती से आवश्यक नहीं है: CTYPE कार्यों के लिए डाली चार मानों को लेकर unsigned char( charOpenBSD पर हस्ताक्षर किया गया है): एक सही कॉल है char c; … iscntrl((unsigned char)c)। उन्हें यह गारंटी देने का लाभ है कि कोई बफर अतिप्रवाह नहीं है: यदि एप्लिकेशन iscntrlउस मान के साथ कॉल करता है जो कि सीमा से बाहर है unsigned charऔर -1 नहीं है, तो यह फ़ंक्शन एक मान देता है जो सार्थक नहीं हो सकता है लेकिन कम से कम इसका कारण नहीं होगा एक दुर्घटना या निजी डेटा का रिसाव जो सरणी सीमा के बाहर पते पर हुआ। फ़ंक्शन के रूप char c; … iscntrl(c)में लंबे समय के रूप में कहा जाता है, तो मान भी सही हैc नहीं है -1।

-1 के साथ विशेष मामले का कारण यह है EOF। कई मानक सी फ़ंक्शन जो एक पर काम करते हैं char, उदाहरण के लिए getchar, चरित्र को एक intमूल्य के रूप में EOF == -1दर्शाते हैं जो एक सकारात्मक सीमा से लिपटे चार मूल्य है, और यह इंगित करने के लिए विशेष मूल्य का उपयोग करते हैं कि कोई चरित्र नहीं पढ़ा जा सकता है। जैसे कार्यों के लिए getchar, EOFफ़ाइल के अंत को इंगित करता है, इसलिए नाम e nd- o f- f ile है। एरिक पोस्टपिसिल का सुझाव है कि कोड मूल रूप से बस थाreturn _ctype_[_c + 1] , और यह शायद सही है:_ctype_[0] लिए मूल्य होगा। यदि फ़ंक्शन का दुरुपयोग किया जाता है, तो यह सरल कार्यान्वयन एक बफर अतिप्रवाह तक पहुंचता है, जबकि वर्तमान कार्यान्वयन इससे ऊपर चर्चा करने से बचता है।

यदि vसरणी में पाया गया मान है,v & _C तो बिट में 0x20सेट होने पर परीक्षण किया जाता है v। सरणी में मान उन श्रेणियों के मुखौटे हैं जिनमें चरित्र है: _Cनियंत्रण वर्णों के _Uलिए सेट किया गया है, अपरकेस अक्षरों के लिए सेट है, आदि।


(_ctype_ + 1)[_c] सी मानक द्वारा निर्दिष्ट सही एरे इंडेक्स का उपयोग करेगा , क्योंकि यह उपयोगकर्ता की जिम्मेदारी है कि वह EOFया तो पास करे या एक unsigned charमूल्य। अन्य मानों के लिए व्यवहार C मानक द्वारा परिभाषित नहीं है। कलाकार सी मानक द्वारा आवश्यक व्यवहार को लागू करने के लिए सेवा नहीं करता है। यह प्रोग्रामर द्वारा बग के कारण नकारात्मक चरित्र मानों को गलत तरीके से पारित करने के लिए गार्ड में रखा गया वर्कअराउंड है। हालाँकि, यह अधूरा या गलत है (और इसे सुधारा नहीं जा सकता है) क्योंकि character1 वर्ण मान आवश्यक रूप से माना जाएगा EOF
एरिक पोस्टपिसिल

यह भी की एक व्याख्या प्रदान करता है + 1। यदि मैक्रो में पहले से यह रक्षात्मक समायोजन नहीं था, तो इसे केवल ((_ctype_+1)[_c] & _C)इस तरह लागू किया जा सकता था , इस प्रकार पूर्व समायोजन मूल्यों के साथ अनुक्रमित तालिका -1 से 255 हो गई। इसलिए पहली प्रविष्टि को छोड़ नहीं दिया गया और एक उद्देश्य पूरा नहीं किया। जब किसी ने बाद में रक्षात्मक कलाकारों को जोड़ा, not1 का EOFमान उस कलाकारों के साथ काम नहीं करेगा, इसलिए उन्होंने विशेष रूप से इलाज करने के लिए सशर्त ऑपरेटर को जोड़ा।
एरिक पोस्टपिसिल

2

मैं चरण 3 से शुरू करूँगा:

पता बढ़ाने के अपरिभाषित सूचक अंक 1 से करने के लिए

सूचक अपरिभाषित नहीं है । यह सिर्फ कुछ अन्य संकलन इकाई में परिभाषित किया गया है। यही तो हैextern हिस्सा जो संकलक को बताता है। इसलिए जब सभी फाइलें एक साथ जुड़ी होती हैं, तो लिंकर इसके संदर्भों को हल कर देगा।

तो इसका क्या मतलब है?

यह प्रत्येक वर्ण के बारे में जानकारी के साथ एक सरणी को इंगित करता है। प्रत्येक चरित्र की अपनी प्रविष्टि है। एक प्रविष्टि चरित्र के लिए विशेषताओं का एक बिटमैप प्रतिनिधित्व है। उदाहरण के लिए: यदि बिट 5 सेट है, तो इसका मतलब है कि चरित्र एक नियंत्रण चरित्र है। एक और उदाहरण: यदि बिट 0 सेट है, तो इसका मतलब है कि चरित्र एक ऊपरी चरित्र है।

तो कुछ ऐसा (_ctype_ + 1)['x']होगा जो विशेषताओं को लागू करेगा 'x'। फिर एक बिटवाइज़ और यह जाँचने के लिए किया जाता है कि क्या बिट 5 सेट है, यानी जाँच करें कि क्या यह एक नियंत्रण वर्ण है।

1 जोड़ने का कारण संभवतः यह है कि वास्तविक सूचकांक 0 किसी विशेष उद्देश्य के लिए आरक्षित है।


1

यहां सभी जानकारी स्रोत कोड (और प्रोग्रामिंग अनुभव) का विश्लेषण करने पर आधारित है।

घोषणा

extern const char *_ctype_;

संकलक को बताता है कि const charकहीं नाम के लिए एक सूचक है _ctype_

(4) यह पॉइंटर ऐरे के रूप में एक्सेस किया जाता है।

(_ctype_ + 1)[(unsigned char)_c]

यह (unsigned char)_cसुनिश्चित करता है कि सूचकांक मान unsigned char(0..255) की सीमा में है ।

सूचक अंकगणित _ctype_ + 1प्रभावी रूप से 1 तत्व द्वारा सरणी स्थिति को स्थानांतरित करता है। मुझे नहीं पता कि उन्होंने इस तरह से सरणी क्यों लागू की। श्रेणी का उपयोग _ctype_[1].. _ctype[256]वर्ण मान के लिए 0.. मान 255छोड़ता है_ctype_[0] इस फ़ंक्शन के लिए अप्रयुक्त । (1 की भरपाई को कई वैकल्पिक तरीकों से लागू किया जा सकता है।)

ऐरे एक्सेस charको कैरेक्टर वैल्यू के रूप में एरे इंडेक्स के रूप में इस्तेमाल करते हुए ( स्पेस को बचाने के लिए) एक मान प्राप्त होता है ।

(5) बिटवाइज़ और ऑपरेशन मान से एक एकल बिट निकालता है।

जाहिरा तौर पर सरणी से मान का उपयोग बिट फ़ील्ड के रूप में किया जाता है जहां बिट 5 (0 से गिनती कम से कम महत्वपूर्ण बिट, = 0x20) "एक नियंत्रण चरित्र है" के लिए एक ध्वज है। इसलिए सरणी में वर्णों के गुणों का वर्णन करने वाले बिट फ़ील्ड मान हैं।


मैं वे चले गए लगता है कि + 1सूचक के लिए यह स्पष्ट है कि वे तत्वों एक्सेस कर रहे हैं बनाने के लिए 1..256की बजाय 1..255,0_ctype_[1 + (unsigned char)_c]के निहितार्थ के कारण समतुल्य होता int। और _ctype_[(_c & 0xff) + 1]भी अधिक स्पष्ट और संक्षिप्त होता।
सेंटास्टर - मोनिका

0

यहां कुंजी यह समझना है कि अभिव्यक्ति क्या करती (_ctype_ + 1)[(unsigned char)_c]है (जो तब बिटवाइज़ और ऑपरेशन को खिलाया जाता है , & 0x20परिणाम प्राप्त करने के लिए!

संक्षिप्त उत्तर: यह _c + 1इंगित किए गए सरणी का तत्व देता है _ctype_

कैसे?

सबसे पहले, हालांकि आपको लगता है कि _ctype_यह अपरिभाषित है कि यह वास्तव में नहीं है! शीर्षलेख इसे बाहरी चर के रूप में घोषित करता है - लेकिन यह रन-टाइम पुस्तकालयों में से एक (लगभग निश्चित रूप से) में परिभाषित किया गया है कि जब आप इसे बनाते हैं तो आपका कार्यक्रम जुड़ा होता है।

वर्णन करने के लिए कि सिंटैक्स कैसे सरणी अनुक्रमण से मेल खाती है, निम्न लघु कार्यक्रम के माध्यम से (यहां तक ​​कि संकलन) काम करने का प्रयास करें:

#include <stdio.h>
int main() {
    // Code like the following two lines will be defined somewhere in the run-time
    // libraries with which your program is linked, only using _ctype_ in place of _qlist_ ...
    const char list[] = "abcdefghijklmnopqrstuvwxyz";
    const char* _qlist_ = list;
    // These two lines show how expressions like (a)[b] and (a+1)[b] just boil down to
    // a[b] and a[b+1], respectively ...
    char p = (_qlist_)[6];
    char q = (_qlist_ + 1)[6];
    printf("p = %c  q = %c\n", p, q);
    return 0;
}

आगे स्पष्टीकरण और / या स्पष्टीकरण के लिए स्वतंत्र महसूस करें।


0

ctype.hप्रकार की वस्तुओं को स्वीकार करने में घोषित कार्य int। तर्कों के रूप में उपयोग किए जाने वाले वर्णों के लिए यह माना जाता है कि वे प्रारंभिक प्रकार के लिए डाले गए हैं unsigned char। इस चरित्र को एक तालिका में एक सूचकांक के रूप में प्रयोग किया जाता है जो चरित्र की विशेषता को निर्धारित करता है।

ऐसा लगता है कि चेक _c == -1का उपयोग उस स्थिति में किया जाता है जब _cमूल्य होता है EOF। यदि यह नहीं है, EOFतो _c को उस प्रकार के अहस्ताक्षरित चार में डाला जाता है, जिसे अभिव्यक्ति द्वारा इंगित तालिका में एक सूचकांक के रूप में उपयोग किया जाता है _ctype_ + 1। और यदि मुखौटा द्वारा निर्दिष्ट बिट 0x20सेट है तो चरित्र एक नियंत्रण प्रतीक है।

अभिव्यक्ति को समझने के लिए

(_ctype_ + 1)[(unsigned char)_c]

इस बात का ध्यान रखें कि सरणी सबस्क्रिप्टिंग एक पोस्टफ़िक्स ऑपरेटर है जिसे इस तरह परिभाषित किया गया है

postfix-expression [ expression ]

आप जैसा नहीं लिख सकते हैं

_ctype_ + 1[(unsigned char)_c]

क्योंकि यह अभिव्यक्ति के बराबर है

_ctype_ + ( 1[(unsigned char)_c] )

तो अभिव्यक्ति _ctype_ + 1 को प्राथमिक अभिव्यक्ति प्राप्त करने के लिए कोष्ठक में संलग्न किया गया है।

तो वास्तव में आपके पास है

pointer[integral_expression]

कि सूचकांक पर एक सरणी के ऑब्जेक्ट की पैदावार होती है जो कि उस integral_expressionसूचक के रूप में गणना की जाती है जहां पॉइंटर है (_ctype_ + 1)(गेयर को पॉइंटर अंकगणित का उपयोग किया जाता है) और integral_expressionयही इंडेक्स अभिव्यक्ति है (unsigned char)_c

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