+++++ b काम क्यों नहीं करता है?


88
int main ()
{
   int a = 5,b = 2;
   printf("%d",a+++++b);
   return 0;
}

यह कोड निम्नलिखित त्रुटि देता है:

त्रुटि: वेतन वृद्धि ऑपरेंड के रूप में आवश्यक है

लेकिन अगर मैं जगह भर में a++ +और डाल दिया ++b, तो यह ठीक काम करता है।

int main ()
{
   int a = 5,b = 2;
   printf("%d",a++ + ++b);
   return 0;
}

पहले उदाहरण में त्रुटि का क्या अर्थ है?


3
यह इस समय के बाद आश्चर्य की बात है कि किसी को भी पता नहीं चला था कि आप जिस सटीक अभिव्यक्ति के बारे में पूछ रहे हैं उसका उपयोग C99 और C11 मानक में एक उदाहरण के रूप में किया जाता है। यह एक अच्छी व्याख्या भी देता है। मैंने अपने उत्तर में इसे शामिल किया है।
शाफिक याघमोर

@ शफीक्यगहमौर - यह 'उदाहरण 2' है C11 Y6.4 लेक्सिकल एलिमेंट्स Y6 में । यह कहता है, "कार्यक्रम के टुकड़े x+++++yको पार्स किया गया है x ++ ++ + y, जो वेतन वृद्धि संचालकों पर एक बाधा का उल्लंघन करता है, भले ही पार्स x ++ + ++ yएक सही अभिव्यक्ति दे सकता है।"
जोनाथन लेफ़लर

जवाबों:


97

printf("%d",a+++++b);(a++)++ + bमैक्सिमल पंच नियम के अनुसार व्याख्या की जाती है !

++(पोस्टफिक्स) का मूल्यांकन नहीं होता है, lvalueलेकिन इसके लिए इसके ऑपरेंड की आवश्यकता होती है lvalue

! 6.4 / 4 का कहना है कि अगला प्रीप्रोसेसिंग टोकन वर्णों का सबसे लंबा अनुक्रम है जो प्रीप्रोसेसिंग टोकन का गठन कर सकता है।


181

कंपाइलर चरणों में लिखे जाते हैं। पहले चरण को लेसर कहा जाता है और पात्रों को एक प्रतीकात्मक संरचना में बदल दिया जाता है। तो "++" कुछ ऐसा ही हो जाता हैenum SYMBOL_PLUSPLUS । बाद में, पार्सर चरण इसे एक सार वाक्यविन्यास पेड़ में बदल देता है, लेकिन यह प्रतीकों को बदल नहीं सकता है। आप रिक्त स्थान डालकर लेसर को प्रभावित कर सकते हैं (जो प्रतीकों को समाप्त करते हैं जब तक कि वे उद्धरणों में नहीं हैं)।

सामान्य लेक्सर्स लालची होते हैं (कुछ अपवादों के साथ), इसलिए आपके कोड की व्याख्या की जा रही है

a++ ++ +b

पार्सर के लिए इनपुट प्रतीकों की एक धारा है, इसलिए आपका कोड कुछ इस तरह होगा:

[ SYMBOL_NAME(name = "a"), 
  SYMBOL_PLUS_PLUS, 
  SYMBOL_PLUS_PLUS, 
  SYMBOL_PLUS, 
  SYMBOL_NAME(name = "b") 
]

जो Parser सोचता है कि वाक्यविन्यास गलत है। (टिप्पणियों पर आधारित EDIT: शब्दशः गलत क्योंकि आप ++ को r- मान पर लागू नहीं कर सकते हैं, जो ++ परिणाम देता है)

a+++b 

है

a++ +b

जो ठीक है। तो आपके अन्य उदाहरण हैं।


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

7
a++ एक परिणाम में परिणाम।
फेमारेफ

9
लेकर्स के संदर्भ में, 'लालची' एल्गोरिथ्म को आमतौर पर मैक्सिमल मंच ( en.wikipedia.org/wiki/Maximal_munch ) कहा जाता है ।
जी.जी.

14
अच्छा लगा। कई भाषाओं में लालची लेक्सिंग के लिए समान विचित्र कोने वाले मामले हैं। यहां एक बहुत अजीब है जहां अभिव्यक्ति को लंबे समय तक बेहतर बना देता है: वीबीएसस्क्रिप्ट x = 10&987&&654&&321में अवैध है, लेकिन विचित्र रूप से पर्याप्त x = 10&987&&654&&&321कानूनी है।
एरिक लिपर्ट

1
इसका लालच और सभी के साथ कोई लेना-देना नहीं है। ++ अधिक है + तो दो ++ पहले किया जाएगा। +++++ b भी + ++ ++ b होगा न कि ++ +++ b। लिंक के लिए @MByD को क्रेडिट।

30

लेसर टोकन का उपयोग करने के लिए आम तौर पर "अधिकतम कूबड़" एल्गोरिथ्म का उपयोग करता है। इसका मतलब है कि जैसे ही यह वर्णों को पढ़ रहा है, यह तब तक वर्णों को पढ़ता रहता है जब तक कि उसका सामना कुछ ऐसा न हो जाए जो उसी टोकन का हिस्सा नहीं हो सकता है जो पहले से ही है (जैसे, यदि वह अंकों को पढ़ रहा है तो उसका क्या नंबर है, अगर उसका सामना होता है a A, यह जानता है कि संख्या का हिस्सा नहीं हो सकता। इसलिए यह रुक जाता है और Aअगले टोकन की शुरुआत के रूप में उपयोग करने के लिए इनपुट बफर में निकल जाता है )। इसके बाद उस टोकन को पार्सर को लौटा देता है।

इस मामले में, इसका मतलब +++++है कि के रूप में lexed जाता है a ++ ++ + b। चूंकि पहली पोस्ट-इन्क्रीमेंट एक पैदावार देती है, दूसरा उस पर लागू नहीं किया जा सकता है, और कंपाइलर एक त्रुटि देता है।

बस एफडब्ल्यूआईडब्ल्यू, सी ++ में आप operator++एक लवल्यू का उत्पादन करने के लिए अधिभार कर सकते हैं , जो इसे काम करने की अनुमति देता है। उदाहरण के लिए:

struct bad_code { 
    bad_code &operator++(int) { 
        return *this;
    }
    int operator+(bad_code const &other) { 
        return 1;
    }
};

int main() { 
    bad_code a, b;

    int c = a+++++b;
    return 0;
}

संकलित करता है और चलाता है (हालांकि यह कुछ भी नहीं करता है) सी ++ संकलक के साथ मेरे पास काम है (वीसी ++, जी ++, कोमो)।


1
"उदाहरण के लिए, यदि यह अंकों को पढ़ रहा है, तो इसकी संख्या क्या है, यदि यह ए का सामना करता है, तो यह जानता है कि संख्या का हिस्सा नहीं हो सकता है" 16FAएक पूरी तरह से ठीक हेक्साडेसिमल संख्या है जिसमें एक ए
ऑर्लप

1
@ नाइटक्रैकर: हाँ, लेकिन 0xशुरुआत में बिना यह अभी भी इलाज करेगा कि 16उसके बाद FA, एक भी हेक्साडेसिमल संख्या नहीं है।
जेरी कॉफिन

@ जेरी कॉफिन: आपने यह नहीं कहा 0xकि संख्या का हिस्सा नहीं था।
19

@ नाइटक्रैकर: नहीं, मैंने ऐसा नहीं किया - अधिकांश लोगों ने xएक अंक पर विचार नहीं किया , यह काफी अनावश्यक लग रहा था।
जेरी कॉफिन

14

यह सटीक उदाहरण मसौदा C99 मानक ( C11 में एक ही विवरण ) खंड 6.4 लेक्सिकल एलिमेंट्स पैराग्राफ 4 में शामिल किया गया है जो कहता है:

यदि इनपुट स्ट्रीम को किसी दिए गए वर्ण तक प्रीप्रोसेसिंग टोकन में पार्स किया गया है, तो अगला प्रीप्रोसेसिंग टोकन वर्णों का सबसे लंबा अनुक्रम है जो प्रीप्रोसेसिंग टोकन का गठन कर सकता है। [...]

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

पैराग्राफ के दो उदाहरण भी हैं, दूसरा प्रश्न के लिए एक सटीक मिलान है और निम्नानुसार है:

उदाहरण 2 कार्यक्रम का टुकड़ा x +++++ y को x ++ ++ + y के रूप में पार्स किया गया है, जो वेतन वृद्धि x + +++ y के लिए एक बाधा पैदा करता है, भले ही पार्स x +++ ++ y एक सही अभिव्यक्ति दे।

जो हमें बताता है कि:

a+++++b

के रूप में पार्स किया जाएगा:

a ++ ++ + b

जो पहले पद वृद्धि के परिणाम के बाद से पोस्ट वेतन वृद्धि पर बाधाओं का उल्लंघन करता है और पोस्ट वेतन वृद्धि के लिए एक अंतराल की आवश्यकता होती है। यह खंड 6.5.2.4 पोस्टफ़िक्स इन्क्रीमेंट और डिक्रीमेंट ऑपरेटरों में शामिल है जो कहते हैं ( जोर मेरा ):

पोस्टफिक्स इन्क्रीमेंट या डिक्रीमेंट ऑपरेटर के ऑपरेटर के पास योग्य या अयोग्य वास्तविक या पॉइंटर प्रकार होगा और एक परिवर्तनीय अंतराल होगा।

तथा

पोस्टफ़िक्स ++ ऑपरेटर का परिणाम ऑपरेंड का मूल्य है।

पुस्तक C ++ गोत्च्स भी इस मामले को Gotcha #17 Maximal Munch Problems में शामिल करती है, C ++ में भी यही समस्या है और यह इसके उदाहरण भी देता है। यह बताता है कि वर्णों के निम्नलिखित सेट से निपटने पर:

->*

शाब्दिक विश्लेषक तीन चीजों में से एक कर सकता है:

  • तीन टोकन के रूप में यह इलाज: -, >और*
  • इसे दो टोकन मानें: ->और*
  • इसे एक टोकन मानें: ->*

अधिक से अधिक मंच नियम यह इन अस्पष्टता से बचने के लिए अनुमति देता है। लेखक बताते हैं कि यह ( C ++ संदर्भ में ):

कई और समस्याओं का कारण बनता है, लेकिन दो सामान्य स्थितियों में, यह एक झुंझलाहट है।

पहला उदाहरण वे टेम्पलेट होंगे जिनके टेम्प्लेट तर्क भी टेम्प्लेट हैं ( जो C ++ 11 में हल किया गया था ), उदाहरण के लिए:

list<vector<string>> lovos; // error!
                  ^^

जो शिफ्ट ऑपरेटर के रूप में समापन कोण कोष्ठक की व्याख्या करता है , और इसलिए स्थान की आवश्यकता होती है:

list< vector<string> > lovos;
                    ^

दूसरे मामले में संकेत के लिए डिफ़ॉल्ट तर्क शामिल हैं, उदाहरण के लिए:

void process( const char *= 0 ); // error!
                         ^^

*=असाइनमेंट ऑपरेटर के रूप में व्याख्या की जाएगी , इस मामले में समाधान घोषणा में मापदंडों का नाम है।


क्या आप जानते हैं कि C ++ 11 का कौन सा भाग अधिकतम कुतरने वाला नियम कहता है? 2.2.3, 2.5.3 दिलचस्प हैं, लेकिन सी के रूप में स्पष्ट नहीं हैं। >>नियम से पूछा जाता है: stackoverflow.com/questions/15785496/…
Ciro Santilli 郝海东 are are are are are

1
@CiroSantilli 巴拿馬 ill ill 法轮功answer यहाँ इस जवाब को
Shafik Yaghmour

अच्छा धन्यवाद, यह उन वर्गों में से एक है जिसकी ओर मैंने इशारा किया। कल जब तुम मेरी टोपी पहनोगे तो मैं तुम्हें उकसाऊंगा ;-)
उकसाऊंगा सिरो सेंटिल्ली ote tomorrow tomorrow tomorrow 六四

12

आपका कंपाइलर पूरी तरह से पार्स करने की कोशिश करता है a+++++b, और इसकी व्याख्या करता है (a++)++ +b। अब, पोस्ट-इन्क्रीमेंट ( a++) का परिणाम एक अंतराल नहीं है , अर्थात इसे फिर से पोस्ट-इंक्रीमेंट नहीं किया जा सकता है।

कृपया उत्पादन गुणवत्ता कार्यक्रमों में कभी भी ऐसा कोड न लिखें। अपने बाद आने वाले गरीब साथी के बारे में सोचें जिन्हें आपके कोड की व्याख्या करने की आवश्यकता है।


10
(a++)++ +b

एक ++ पिछला मान लौटाता है, एक प्रतिद्वंद्विता। आप इसे नहीं बढ़ा सकते।


7

क्योंकि यह अपरिभाषित व्यवहार का कारण बनता है।

इनमें से कौनसा?

c = (a++)++ + b
c = (a) + ++(++b)
c = (a++) + (++b)

हाँ, न तो आप और न ही संकलक इसे जानते हैं।

संपादित करें:

वास्तविक कारण दूसरों के द्वारा कहा गया है:

इसकी व्याख्या हो जाती है (a++)++ + b

लेकिन पोस्ट वेतन वृद्धि के लिए एक अंतराल की आवश्यकता होती है (जो एक नाम के साथ एक चर है) लेकिन (a ++) एक अंतराल देता है जिसे बढ़ाया नहीं जा सकता है जिससे आपको प्राप्त होने वाले त्रुटि संदेश के लिए अग्रणी होता है।

इसे इंगित करने के लिए दूसरों को Thx।


5
आप +++ b - (a ++) + b और a + (++ b) के लिए अलग-अलग परिणाम कह सकते हैं।
माइकल चिनन

4
वास्तव में, पोस्टफिक्स ++ में प्रीफिक्स ++ की तुलना में अधिक पूर्वता है, इसलिए a+++bहमेशाa++ + b
MB16D 15'11

4
मुझे नहीं लगता कि यह सही उत्तर है, लेकिन मैं गलत हो सकता है। मुझे लगता है कि लेक्सर इसे परिभाषित करता है a++ ++ +bजिसे पार्स नहीं किया जा सकता है।
लो फ्रेंको

2
मैं इस जवाब से असहमत हूं। 'अपरिभाषित व्यवहार', टोकन अस्पष्टता से काफी अलग है; और मुझे नहीं लगता कि समस्या या तो है।
जिम ब्लैकलर

2
"अन्यथा एक +++++ b ((a ++) ++) + b" का मूल्यांकन करेगा ... मेरा दृष्टिकोण अभी मूल्यांकन a+++++b करता है (a++)++)+b। निश्चित रूप से जीसीसी के साथ यदि आप उन ब्रैकेट को सम्मिलित करते हैं और पुनर्निर्माण करते हैं, तो त्रुटि संदेश नहीं बदलता है।
जिम ब्लैकलर

5

मुझे लगता है कि कंपाइलर इसे देखता है

c = ((a ++) ++) + b

++एक ऑपरेंड के रूप में एक मूल्य है जिसे संशोधित किया जा सकता है। a एक मूल्य है जिसे संशोधित किया जा सकता है। a++हालाँकि एक 'प्रतिद्वंद्विता' है, इसे संशोधित नहीं किया जा सकता है।

जिस तरह से मैं जीसीसी सी पर त्रुटि देखता हूं वह एक ही है, लेकिन अलग-अलग शब्द lvalue required as increment operand:।


0

इस पूर्व आदेश का पालन करें

1। ++ (पूर्व वेतन वृद्धि)

2. + - (जोड़ या घटाव)

3. "x" + "y" दोनों अनुक्रम जोड़ें

int a = 5,b = 2; printf("%d",a++ + ++b); //a is 5 since it is post increment b is 3 pre increment return 0; //it is 5+3=8

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