सी में कोई प्रभाव नहीं के साथ बयान को कानूनी क्यों माना जाता है?


13

क्षमा करें यदि यह प्रश्न भोला है। निम्नलिखित कार्यक्रम पर विचार करें:

#include <stdio.h>

int main() {
  int i = 1;
  i = i + 2;
  5;
  i;
  printf("i: %d\n", i);
}

उपर्युक्त उदाहरण में, कथनों 5;और i;पूरी तरह से अतिश्योक्तिपूर्ण लगता है, फिर भी कोड डिफ़ॉल्ट रूप से चेतावनियों या त्रुटियों के बिना संकलित करता है (हालांकि, जीसीसी एक warning: statement with no effect [-Wunused-value]चेतावनी को फेंक देता है जब साथ चलाया जाता है -Wall)। बाकी कार्यक्रम पर उनका कोई प्रभाव नहीं है, इसलिए उन्हें पहली बार में मान्य बयान क्यों माना जाता है? क्या कंपाइलर बस उन्हें अनदेखा करता है? क्या ऐसे बयानों की अनुमति देने का कोई लाभ है?


5
ऐसे बयानों पर प्रतिबंध लगाने के क्या लाभ हैं?
मूविंग डक

2
कोई भी अभिव्यक्ति ;उसके बाद रख कर बयान दे सकता है। यह भाषा को और अधिक नियम जोड़ने के लिए जटिल करेगा जब अभिव्यक्तियों को बयान नहीं किया जा सकता है
एमएम

3
क्या आप बल्कि अपने कोड को संकलित करने में विफल हो सकते हैं क्योंकि आप के वापसी मूल्य को अनदेखा करते हैं printf()? बयान 5;मूल रूप से कहते हैं, "जो कुछ भी करना 5है (कुछ भी नहीं) और परिणाम पर ध्यान न दें। अपने बयान printf(...)कि" जो कुछ भी करना printf(...)होता है और परिणाम की अनदेखी (से वापसी मान printf()) "। सी उन्हीं व्यवहार करता है। यह भी इस तरह के रूप कोड के लिए अनुमति देता है (void) i;जहां iहै एक फ़ंक्शन के लिए एक पैरामीटर जिसे आप voidइसे जानबूझकर अप्रयुक्त के रूप में चिह्नित करने के लिए
डालते हैं

1
@AndrewHenle: यह काफी समान नहीं है, क्योंकि कॉलिंग printf()का प्रभाव पड़ता है, भले ही आप उस मूल्य को अनदेखा कर दें जो अंततः लौटता है। इसके विपरीत 5;कोई प्रभाव नहीं पड़ता है।
नैट एल्ड्रेडगे

1
क्योंकि डेनिस रिची, और वह हमें बताने के लिए आसपास नहीं है।
user207421

जवाबों:


10

इस तरह के बयानों की अनुमति देने का एक फायदा कोड से है जो मनुष्यों द्वारा लिखे जाने के बजाय मैक्रोज़ या अन्य कार्यक्रमों द्वारा बनाया गया है।

एक उदाहरण के रूप में, एक फ़ंक्शन की कल्पना करें int do_stuff(void)जिसे सफलता पर 0 या विफलता पर -1 माना जाता है। यह हो सकता है कि "सामान" के लिए समर्थन वैकल्पिक है, और इसलिए आपके पास एक हेडर फ़ाइल हो सकती है जो करती है

#if STUFF_SUPPORTED
#define do_stuff() really_do_stuff()
#else
#define do_stuff() (-1)
#endif

अब कुछ कोड की कल्पना करें जो संभव हो तो सामान करना चाहते हैं, लेकिन वास्तव में परवाह कर सकते हैं या नहीं कि क्या यह सफल होता है या विफल:

void func1(void) {
    if (do_stuff() == -1) {
        printf("stuff did not work\n");
    }
}

void func2(void) {
    do_stuff(); // don't care if it works or not
    more_stuff();
}

जब STUFF_SUPPORTED0 होता है, तो प्रीप्रोसेसर func2उस स्टेटमेंट में कॉल का विस्तार करेगा जो सिर्फ पढ़ता है

    (-1);

और इसलिए कंपाइलर पास में "सुपरफ्लस" स्टेटमेंट दिखाई देगा जो आपको परेशान करता है। फिर भी कोई और क्या कर सकता है? यदि आप #define do_stuff() // nothing, तो कोड में func1टूट जाएगा। (और आपके पास अभी भी एक खाली बयान होगा func2जिसमें सिर्फ लिखा है ;, जो शायद और भी अधिक शानदार है।) दूसरी ओर, अगर आपको वास्तव में एक do_stuff()फ़ंक्शन को परिभाषित करना है जो -1 रिटर्न करता है, तो आप एक फ़ंक्शन कॉल की लागत का भुगतान कर सकते हैं बिना किसी अच्छे कारण के।


नो-ऑप का एक और अधिक क्लासिक संस्करण (या मेरा मतलब सामान्य संस्करण है) ((void)0)
जोनाथन लेफ्लर

इसका एक अच्छा उदाहरण है assert
नील

3

सी में सरल विवरण अर्धविराम द्वारा समाप्त किए जाते हैं।

C में सरल कथन अभिव्यक्ति हैं। एक अभिव्यक्ति चर, स्थिरांक और ऑपरेटरों का एक संयोजन है। प्रत्येक अभिव्यक्ति का एक निश्चित प्रकार के कुछ मूल्य में परिणाम होता है जिसे एक चर को सौंपा जा सकता है।

कहा कि कुछ "स्मार्ट कंपाइलर" 5 को त्याग सकते हैं; और मैं; बयान।


मैं किसी भी संकलक की कल्पना नहीं कर सकता जो उन बयानों के साथ कुछ भी करेगा उन्हें त्यागने के अलावा। यह उनके साथ और क्या कर सकता था?
जेरेमी फ्रेज़र

@JeremyFriesner: एक बहुत ही सरल, गैर-अनुकूलन करने वाला कंपाइलर मूल्य की गणना करने के लिए कोड उत्पन्न कर सकता है और परिणाम को एक रजिस्टर में डाल सकता है (जिस बिंदु से इसे अनदेखा किया जाएगा)।
नैट एल्ड्रेडगे

C मानक शब्द "सरल कथन" नहीं है। एक अभिव्यक्ति कथन में एक (वैकल्पिक) अभिव्यक्ति होती है, जिसके बाद अर्धविराम होता है। प्रत्येक अभिव्यक्ति का मूल्य नहीं होता है; प्रकार voidकी अभिव्यक्ति का कोई मूल्य नहीं है।
कीथ थॉम्पसन

2

बिना प्रभाव वाले विवरणों की अनुमति नहीं है क्योंकि उन्हें अनुमति देने की तुलना में उन पर प्रतिबंध लगाना अधिक कठिन होगा। यह तब अधिक प्रासंगिक था जब सी को पहली बार डिजाइन किया गया था और संकलक छोटे और सरल थे।

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

उदाहरण के लिए, एक फ़ंक्शन कॉल एक अभिव्यक्ति है, इसलिए अर्धविराम के बाद एक फ़ंक्शन कॉल एक बयान है। क्या इस कथन का कोई दुष्प्रभाव है?

some_function();

के कार्यान्वयन को देखे बिना बताना असंभव है some_function

इस बारे में कैसा है?

obj;

शायद नहीं - लेकिन अगर objइसे परिभाषित किया जाता है volatile, तो यह करता है।

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


2

बिना किसी प्रभाव के आपके द्वारा सूचीबद्ध कथन एक अभिव्यक्ति कथन के उदाहरण हैं , जिसका सिंटैक्स C मानक के 6.8.3p1 में दिया गया है:

 अभिव्यक्ति-बयान :
    अभिव्यक्ति विकल्प  ;

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

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

i = i + 2;
5;
i;
printf("i: %d\n", i);

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

यहाँ एक और उदाहरण है:

atoi("1");

यह एक फ़ंक्शन को कॉल कर रहा है और परिणाम को त्याग रहा है, जैसे printfआपके उदाहरण में कॉल लेकिन printfफ़ंक्शन कॉल के विपरीत स्वयं का साइड इफेक्ट नहीं होता है।


1

कभी-कभी ऐसे बयान बहुत काम आते हैं:

int foo(int x, int y, int z)
{
    (void)y;   //prevents warning
    (void)z;

    return x*x;
}

या जब संदर्भ मैनुअल हमें बताता है कि रजिस्टर को केवल कुछ संग्रह करने के लिए पढ़ें - उदाहरण के लिए स्पष्ट करने के लिए या कुछ ध्वज सेट करने के लिए (uv दुनिया में बहुत सामान्य स्थिति)

#define SREG   ((volatile uint32_t *)0x4000000)
#define DREG   ((volatile uint32_t *)0x4004000)

void readSREG(void)
{
    *SREG;   //we read it here
    *DREG;   // and here
}

https://godbolt.org/z/6wjh_5


जब *SREGअस्थिर होता है, *SREG;तो सी मानक द्वारा निर्दिष्ट मॉडल में कोई प्रभाव नहीं पड़ता है। सी मानक निर्दिष्ट करता है कि यह एक नमूदार साइड इफेक्ट है।
एरिक पोस्टपिसिल

@EricPostpischil - नहीं, इसका अवलोकन प्रभाव नहीं है , लेकिन यदि इसका प्रभाव है। C दृश्यमान वस्तुओं में से कोई भी परिवर्तित नहीं हुई है।
P__J__

सी 2018 5.1.2.3 6 कार्यक्रम के अवलोकनीय व्यवहार को परिभाषित करता है जिसमें यह शामिल है कि "अमूर्त मशीन के नियमों के अनुसार अस्थिर वस्तुओं तक पहुंच का कड़ाई से मूल्यांकन किया जाता है।" व्याख्या या कटौती का कोई सवाल ही नहीं है; यह अवलोकनीय व्यवहार की परिभाषा है।
एरिक पोस्टपिसिल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.