क्या शॉर्ट-सर्कुलेटिंग लॉजिकल ऑपरेटर्स अनिवार्य है? और मूल्यांकन क्रम?


140

क्या ANSI मानक तार्किक परिचालकों को C या C ++ में शॉर्ट-सर्कुलेट करने का आदेश देता है?

मुझे भ्रम है कि मैं K & R पुस्तक को यह कहते हुए याद करूंगा कि आपका कोड इन परिचालनों पर निर्भर नहीं होना चाहिए, क्योंकि वे नहीं कर सकते हैं। क्या कोई यह बता सकता है कि मानक में यह कहां कहा जाता है कि लॉजिक ऑप्स हमेशा शॉर्ट-सर्किट होते हैं? मैं ज्यादातर सी ++ पर दिलचस्पी रखता हूं, सी के लिए भी एक जवाब बहुत अच्छा होगा।

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

क्या मानक इस अभिव्यक्ति के मूल्यांकन क्रम को दर्शाता है?

if( functionA() && functionB() && functionC() ) cout<<"Hello world";

12
Carefull: यह POD प्रकारों के लिए सही है। लेकिन अगर आप ऑपरेटर && या ऑपरेटर को ओवरलोड करते हैं || किसी विशेष वर्ग के लिए ये नहीं हैं मैं दोहराए नहीं शॉर्टकट। यही कारण है कि यह सलाह दी जाती है कि आप इन ऑपरेटरों को अपनी कक्षाओं के लिए परिभाषित न करें।
मार्टिन यॉर्क

मैंने कुछ समय पहले इन ऑपरेटरों को फिर से परिभाषित किया, जब मैंने एक वर्ग बनाया जो कुछ बुनियादी बूलियन बीजगणित संचालन करेगा। शायद एक चेतावनी टिप्पणी छड़ी चाहिए "यह कम सर्कुलेटिंग और बाएं-दाएं मूल्यांकन को नष्ट कर देता है!" अगर मैं इसे भूल जाऊं। इसके अलावा * / + को ओवरलोड किया और उन्हें अपना पर्यायवाची बना दिया :-)
जो पीएना

अगर-ब्लॉक में फ़ंक्शन कॉल करना एक अच्छा प्रोग्रामिंग अभ्यास नहीं है। हमेशा एक चर घोषित किया जाता है जो विधि के रिटर्न मान को रखता है और यदि-ब्लॉक में इसका उपयोग करता है।
एसआर चैतन्य

6
@ श्रीचैतन्य यह सही नहीं है। खराब अभ्यास के रूप में आप मनमाने ढंग से वर्णन करते हैं, विशेष रूप से उन कार्यों के साथ किया जाता है जो बूलियनों को वापस लौटाते हैं।
लोरेन

जवाबों:


154

हां, ऑपरेटरों ||और &&C और C ++ दोनों मानकों में शॉर्ट-सर्कुलेटिंग और मूल्यांकन आदेश आवश्यक हैं ।

C ++ मानक कहता है (C मानक में समतुल्य खंड होना चाहिए):

1.9.18

निम्नलिखित भावों के मूल्यांकन में

a && b
a || b
a ? b : c
a , b

इन अभिव्यक्तियों में ऑपरेटरों के अंतर्निहित अर्थ का उपयोग करते हुए, पहली अभिव्यक्ति (12) के मूल्यांकन के बाद एक अनुक्रम बिंदु है

C ++ में एक अतिरिक्त जाल है: शॉर्ट-सर्कुलेटिंग उन प्रकारों पर लागू नहीं होता है जो ओवरलोड ऑपरेटरों ||और &&

फुटनोट 12: इस पैराग्राफ में दर्शाए गए ऑपरेटर अंतर्निहित ऑपरेटर हैं, जैसा कि खंड 5 में वर्णित है। जब इन ऑपरेटरों में से एक को वैध संदर्भ में अधिभार (खंड 13) दिया जाता है, इस प्रकार उपयोगकर्ता-परिभाषित ऑपरेटर फ़ंक्शन को नामित करता है, तो अभिव्यक्ति नामित होती है। एक फ़ंक्शन मंगलाचरण, और ऑपरेंड एक तर्क सूची बनाते हैं, उनके बीच निहित अनुक्रम बिंदु के बिना।

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


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

4
हाँ, यह तर्कसंगत है। यह ऑपरेटर और तर्क (ए, बी) के तर्क के रूप में कार्य कर रहा है। यह इसका कार्यान्वयन है जो कहता है कि क्या होता है।
जोहान्स स्काउब -

10
लिटब: इसका मूल्यांकन किए बिना बस ऑपरेटर और बी (ए, बी) को पास करना संभव नहीं है। और बी का मूल्यांकन करने का कोई तरीका नहीं है क्योंकि संकलक गारंटी नहीं दे सकता कि दुष्प्रभाव नहीं हैं।
jmucchiello

2
मुझे यह दुखद लगता है। मैंने सोचा होगा कि, क्या मुझे ऑपरेटरों और& को फिर से परिभाषित करना चाहिए और || और वे अभी भी पूरी तरह से निर्धारक हैं , संकलक का पता लगाने और उनके मूल्यांकन को कम रखने के लिए होगा: आखिरकार, आदेश अप्रासंगिक है, वे कोई साइड इफेक्ट की गारंटी नहीं देते हैं!
जो पिनेडा

2
@ जो: लेकिन रिटर्न वैल्यू और ऑपरेटर के तर्क बूलियन से बदलकर कुछ और हो सकते हैं। मैं तीन विशेष मूल्यों ("सच", "गलत" और "अज्ञात") के साथ "विशेष" तर्क को लागू करता था। वापसी मूल्य नियतात्मक है, लेकिन शॉर्ट-सर्कुलेटिंग व्यवहार उचित नहीं है।
एलेक्स बी

70

शॉर्ट सर्किट मूल्यांकन, और मूल्यांकन के क्रम, सी और सी ++ दोनों में एक अनिवार्य अर्थ मानक है।

यदि ऐसा नहीं होता, तो इस तरह का कोड एक सामान्य मुहावरा नहीं होगा

   char* pChar = 0;
   // some actions which may or may not set pChar to something
   if ((pChar != 0) && (*pChar != '\0')) {
      // do something useful

   }

धारा 6.5.13 तार्किक और C99 विनिर्देश (पीडीएफ लिंक) के ऑपरेटर का कहना है

(4)। बिटवाइज़ बाइनरी और ऑपरेटर के विपरीत, && ऑपरेटर बाएं से दाएं मूल्यांकन की गारंटी देता है; पहले ऑपरेंड के मूल्यांकन के बाद एक अनुक्रम बिंदु है। यदि पहला ऑपरेंड 0 के बराबर है, तो दूसरे ऑपरेंड का मूल्यांकन नहीं किया जाता है।

इसी तरह, खंड 6.5.14 लॉजिकल या ऑपरेटर कहता है

(४) बिटवाइज के विपरीत | ऑपरेटर, || ऑपरेटर बाएं से दाएं मूल्यांकन की गारंटी देता है; पहले ऑपरेंड के मूल्यांकन के बाद एक अनुक्रम बिंदु है। यदि पहला ऑपरेंड असमान की तुलना 0 से करता है, तो दूसरे ऑपरेंड का मूल्यांकन नहीं किया जाता है।

इसी तरह के शब्दों को C ++ मानकों में पाया जा सकता है, इस ड्राफ्ट कॉपी में खंड 5.14 की जांच करें । चेकर्स एक अन्य उत्तर में नोट करते हैं, यदि आप ओवरराइड && या ||, तो दोनों ऑपरेंड का मूल्यांकन किया जाना चाहिए क्योंकि यह एक नियमित फ़ंक्शन कॉल बन जाता है।


आह, मैं क्या देख रहा था! ठीक है, इसलिए एएनएसआई-सी 99 के अनुसार मूल्यांकन आदेश और शॉर्ट-सर्कुलेटिंग दोनों अनिवार्य हैं! मैं वास्तव में ANSI-C ++ के लिए तेह समकक्ष संदर्भ देखना पसंद करूंगा, हालांकि मैं लगभग 99% हूं यह समान होना चाहिए।
जो पिनेडा

सी ++ मानकों के लिए एक अच्छा मुफ्त लिंक खोजने के लिए मुश्किल है, मैंने कुछ डॉगलिंग के साथ मिली ड्राफ्ट कॉपी से जोड़ा है।
पॉल डिक्सन

POD प्रकारों के लिए सही है। लेकिन अगर आप ऑपरेटर && या ऑपरेटर को ओवरलोड करते हैं || ये शॉर्टकट नहीं हैं।
मार्टिन यॉर्क

1
हाँ यह ध्यान रखना दिलचस्प है कि बूल के लिए, आपको हमेशा मूल्यांकन आदेश और शॉर्ट सर्किट व्यवहार की गारंटी होगी। क्योंकि आप दो प्रकारों के लिए ऑपरेटर और& को अधिभार नहीं दे सकते। आपको अलग-अलग व्यवहार करने के लिए ऑपरेंड्स में कम से कम एक उपयोगकर्ता परिभाषित प्रकार की आवश्यकता होती है।
जोहान्स शहाब -

काश मैं दोनों चेकर्स और इस जवाब को स्वीकार कर सकता। चूंकि मुझे ज्यादातर C ++ में दिलचस्पी है, इसलिए मैं दूसरे को स्वीकार कर रहा हूं, हालांकि यह मानना ​​होगा कि यह बहुत शानदार है! आपका बहुत बहुत धन्यवाद!
जो पिनाडा

19

हां, यह अनिवार्य है कि (मूल्यांकन आदेश और शॉर्ट सर्किट दोनों)। आपके उदाहरण में, यदि सभी फ़ंक्शन सही हैं, तो कॉल का क्रम फ़ंक्शन से सख्ती से कार्य करता है फिर फ़ंक्शनबी और फिर फ़ंक्शनसी। इस तरह के लिए इस्तेमाल किया

if(ptr && ptr->value) { 
    ...
}

अल्पविराम ऑपरेटर के लिए भी:

// calls a, then b and evaluates to the value returned by b
// which is used to initialize c
int c = (a(), b()); 

एक को छोड़ दिया और सही के संकार्य के बीच कहते हैं &&, ||, ,और पहले और दूसरे / तीसरे संकार्य के बीच ?:(सशर्त ऑपरेटर) एक "अनुक्रम बिंदु" है। किसी भी दुष्प्रभाव का उस बिंदु से पहले पूरी तरह से मूल्यांकन किया जाता है। तो, यह सुरक्षित है:

int a = 0;
int b = (a++, a); // b initialized with 1, and a is 1

ध्यान दें कि अल्पविराम ऑपरेटर को अलग-अलग चीजों के लिए उपयोग किए जाने वाले वाक्यविन्यास अल्पविराम के साथ भ्रमित नहीं होना है:

// order of calls to a and b is unspecified!
function(a(), b());

C ++ मानक कहता है 5.14/1:

&& ऑपरेटर समूह बाएं-से-दाएं। ऑपरेंड दोनों को स्पष्ट रूप से टाइप बूल (खंड 4) में परिवर्तित किया जाता है। परिणाम सही है अगर दोनों ऑपरेंड सही और गलत हैं। इसके विपरीत, && बाएं-से-दाएं मूल्यांकन की गारंटी देता है: पहला ऑपरेंड झूठा होने पर दूसरे ऑपरेंड का मूल्यांकन नहीं किया जाता है।

और में 5.15/1 :

|| ऑपरेटर समूह बाएं-दाएं। ऑपरेंड दोनों को स्पष्ट रूप से बूल (खंड 4) में बदल दिया जाता है। यह सच है अगर इसके ऑपरेंड्स में से कोई भी सच है, और अन्यथा गलत है। विपरीत, || बाएं से दाएं मूल्यांकन की गारंटी देता है; इसके अलावा, दूसरे ऑपरेंड का मूल्यांकन नहीं किया जाता है यदि पहला ऑपरेंड सच का मूल्यांकन करता है।

यह उन दोनों के लिए कहता है:

परिणाम एक बूल है। अस्थायी अभिव्यक्ति के विनाश (12.2) को छोड़कर पहली अभिव्यक्ति के सभी दुष्प्रभाव दूसरी अभिव्यक्ति के मूल्यांकन से पहले होते हैं।

इसके अतिरिक्त, 1.9/18 कहते हैं

प्रत्येक भाव के मूल्यांकन में

  • a && b
  • a || b
  • a ? b : C
  • a , b

इन अभिव्यक्तियों में ऑपरेटरों के अंतर्निहित अर्थ का उपयोग करते हुए (5.14, 5.15, 5.16, 5.18), पहले अभिव्यक्ति के मूल्यांकन के बाद एक अनुक्रम बिंदु है।


10

अच्छे पुराने कश्मीर और आर से सीधे:

C गारंटी देता है कि &&और ||सही से बाएं मूल्यांकन किया जाता है - हम जल्द ही ऐसे मामलों को देखेंगे जहां यह मायने रखता है।


3
K & R 2nd संस्करण p40। "&& या || से जुड़े भावों का मूल्यांकन बाएं से दाएं किया जाता है, और परिणाम का सत्य या असत्य ज्ञात होते ही मूल्यांकन रुक जाता है। अधिकांश C प्रोग्राम इन गुणों पर भरोसा करते हैं।" मुझे पुस्तक में आपका उद्धृत पाठ कहीं भी नहीं मिल रहा है। क्या यह अत्यंत अप्रचलित 1 संस्करण से है? कृपया स्पष्ट करें कि आपको यह पाठ कहां मिला है।
लंडिन

1
ठीक है, आप इस प्राचीन ट्यूटोरियल को उद्धृत कर रहे हैं । यह 1974 से है और अत्यधिक अप्रासंगिक है।
लुंडिन

6

बहुत सावधान रहें।

मूलभूत प्रकारों के लिए ये शॉर्टकट ऑपरेटर हैं।

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

के लिए operator &&और operator ||मूल प्रकारों मूल्यांकन आदेश सही (अन्यथा कम काटने के लिए छोड़ दिया जाता है मुश्किल होगा के लिए :-) लेकिन अतिभारित ऑपरेटरों आपके द्वारा निर्धारित, इन एक विधि को परिभाषित करने के लिए मूल रूप से वाक्यात्मक चीनी और इस तरह मापदंडों का मूल्यांकन है के आदेश हैं अपरिभाषित।


1
ऑपरेटर ओवरलोडिंग का POD होने या न होने से कोई लेना-देना नहीं है। ऑपरेटर फ़ंक्शन को परिभाषित करने के लिए, कम से कम एक तर्क के लिए एक वर्ग (या संरचना या संघ) या एक एनम, या उनमें से किसी एक का संदर्भ होना चाहिए। POD होने का मतलब है कि आप इस पर मेमसीपी का इस्तेमाल कर सकते हैं।
डेरेक लेडबेटर

और वही मैं कह रहा था। यदि आप अपनी कक्षा के लिए अधिक भार लेते हैं तो यह वास्तव में एक विधि कॉल है। इस प्रकार आप मापदंडों के मूल्यांकन के क्रम पर भरोसा नहीं कर सकते। जाहिर है कि आप POD प्रकारों के लिए अधिभार && नहीं ले सकते।
मार्टिन यॉर्क

3
आप "POD प्रकार" शब्द का गलत तरीके से उपयोग कर रहे हैं। आप किसी भी संरचना, वर्ग, संघ या enum, POD या नहीं के लिए अधिभार && कर सकते हैं। यदि दोनों पक्ष संख्यात्मक प्रकार या संकेत हैं, तो आप && को अधिभार नहीं दे सकते।
डेरेक लेडबेटेर

मैं POD का उपयोग कर रहा था (char / int / float etc) नहीं एक aggrigate POD (जो आप के बारे में बात कर रहे हैं) और आमतौर पर इसे स्पष्ट रूप से या अधिक स्पष्ट रूप से संदर्भित किया जाता है क्योंकि यह एक प्रकार में निर्मित नहीं है।
मार्टिन यॉर्क

2
तो आपका मतलब "मौलिक प्रकार" था लेकिन "POD प्रकार" लिखा था?
43ö तिएब

0

आपका प्रश्न C ++ ऑपरेटर की पूर्ववर्तीता और सहक्रियाशीलता के लिए नीचे आता है । मूल रूप से, कई ऑपरेटरों और कोई कोष्ठक के साथ अभिव्यक्ति में, कंपाइलर इन नियमों का पालन करके अभिव्यक्ति ट्री का निर्माण करता है।

पूर्वता लिए, जब आप की तरह कुछ है A op1 B op2 C, तो आप कर सकते थे समूह चीजों के रूप में या तो (A op1 B) op2 Cया A op1 (B op2 C)। अगर op1तुलना में अधिक पूर्वता है op2, तो आपको पहले अभिव्यक्ति मिल जाएगा। अन्यथा, आपको दूसरा मिलेगा।

सहानुभूति के लिए, जब आपके पास कुछ होता है A op B op C, तो आप फिर से (A op B) op Cया के रूप में समूह thins कर सकते हैं A op (B op C)। यदि opसहानुभूति छोड़ दी है, तो हम पहली अभिव्यक्ति के साथ समाप्त होते हैं। यदि इसमें सही सहानुभूति है, तो हम दूसरे के साथ समाप्त होते हैं। यह भी इसी पूर्ववर्ती स्तर पर ऑपरेटरों के लिए काम करता है।

इस विशेष मामले में, &&की तुलना में अधिक पूर्वता है ||, इसलिए अभिव्यक्ति का मूल्यांकन किया जाएगा(a != "" && it == seqMap.end()) || isEven

आदेश स्वयं अभिव्यक्ति-वृक्ष के रूप में "बाएं से दाएं" है। तो हम पहले मूल्यांकन करेंगे a != "" && it == seqMap.end()। यदि यह सही है तो पूरी अभिव्यक्ति सत्य है, अन्यथा हम जाते हैं isEven। प्रक्रिया खुद को पाठ्यक्रम के बाएं-उप-संपीड़न के भीतर पुनरावृत्ति दोहराती है।


दिलचस्प टिडबिट्स, लेकिन पूर्वकल्पना की अवधारणा की जड़ें गणितीय संकेतन में हैं। इसमें वही होता है a*b + c, जहां *पहले की तुलना में उच्चता होती है +

एक और भी दिलचस्प / अस्पष्ट, एक अनैच्छिक अभिव्यक्ति के लिए A1 op1 A2 op2 ... opn-1 An, जहां सभी ऑपरेटरों की एक ही मिसाल होती है, हमारे द्वारा बनाए जा सकने वाले बाइनरी एक्सप्रेशन ट्री की संख्या तथाकथित कैटलन संख्याओं द्वारा दी जाती है । बड़े लोगों के लिए n, ये बहुत तेजी से बढ़ते हैं। घ


यह सब सही है, लेकिन यह ऑपरेटर की पूर्ववर्तीता और सहानुभूति के बारे में है, न कि मूल्यांकन के आदेश और लघु-वक्रता के बारे में। वे अलग चीजें हैं।
थॉमस पैड्रॉन-मैक्कार्थी

0

यदि आप विकिपीडिया पर भरोसा करते हैं:

[ &&और |||] बिट के लिहाज से ऑपरेटरों और और से शब्दार्थ अलग हैं क्योंकि वे कभी भी सही ऑपरेंड का मूल्यांकन नहीं करेंगे यदि परिणाम अकेले बाईं ओर से निर्धारित किया जा सकता है

सी (प्रोग्रामिंग भाषा)


11
जब हमारे पास मानक है तो विकी पर भरोसा क्यों करें!
मार्टिन यॉर्क

1
यदि आप विकिपीडिया पर भरोसा करते हैं, तो 'विकिपीडिया एक विश्वसनीय संसाधन नहीं है'
लोर्ने

यह सच है जहाँ तक यह जाता है, लेकिन अधूरा है, क्योंकि C ++ में ओवरलोड ऑपरेटरों को शॉर्ट-सर्कुलेट नहीं किया जाता है।
थॉमस पैड्रॉन-मैक्कार्थी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.