इस 1988 सी कोड में क्या गलत है?


94

मैं "द सी प्रोग्रामिंग लैंग्वेज" (K & R) पुस्तक के कोड के इस टुकड़े को संकलित करने की कोशिश कर रहा हूं। यह UNIX कार्यक्रम का एक नंगे हड्डियों वाला संस्करण है wc:

#include <stdio.h>

#define IN   1;     /* inside a word */
#define OUT  0;     /* outside a word */

/* count lines, words and characters in input */
main()
{
    int c, nl, nw, nc, state;

    state = OUT;
    nl = nw = nc = 0;
    while ((c = getchar()) != EOF) {
        ++nc;
        if (c == '\n')
            ++nl;
        if (c == ' ' || c == '\n' || c == '\t')
            state = OUT;
        else if (state == OUT) {
            state = IN;
            ++nw;
        }
    }
    printf("%d %d %d\n", nl, nw, nc);
}

और मुझे निम्नलिखित त्रुटि मिल रही है:

$ gcc wc.c 
wc.c: In function main’:
wc.c:18: error: else without a previous if
wc.c:18: error: expected ‘)’ before ‘;’ token

इस पुस्तक का दूसरा संस्करण 1988 से है और मैं सी के लिए बहुत नया हूँ। शायद यह संकलक संस्करण के साथ करना है या शायद मैं सिर्फ बकवास कर रहा हूं।

मैंने आधुनिक सी कोड में mainफ़ंक्शन का एक अलग उपयोग देखा है :

int main()
{
    /* code */
    return 0;
}

क्या यह एक नया मानक है या मैं अभी भी टाइप-कम मुख्य का उपयोग कर सकता हूं?


4
उत्तर नहीं, लेकिन अधिक बारीकी से देखने के लिए कोड का एक और टुकड़ा || c = '\t')। क्या यह उस पंक्ति के अन्य कोड के समान प्रतीत होता है?
user7116

58
डिबगिंग + टाइपो प्रश्न के लिए 32 upvotes ?!
ऑर्बिट

37
@ TomalakGeret'kal: आप जानते हैं, पुराने सामान का मूल्य अधिक है (शराब, पेंटिंग, सी कोड)
सर्जियो तुलेंत्सेव

16
@ सेसर: मैं अपनी राय व्यक्त करने के अपने अधिकारों के लिए काफी भीतर हूं, और मैं आपको धन्यवाद दूंगा कि आप इसे सेंसर करने की कोशिश न करें। जैसा कि होता है, हाँ, यह आपके कोड को डीबग करने और आपकी टाइपोग्राफिकल त्रुटियों को हल करने के लिए एक वेबसाइट नहीं है, जो "स्थानीयकृत" मुद्दे हैं जो कभी किसी और की मदद नहीं करेंगे। यह प्रोग्रामिंग भाषाओं के बारे में सवालों के लिए एक वेबसाइट है , न कि आपके लिए अपने मूल डिबगिंग और संदर्भ कार्य करने के लिए। कौशल स्तर पूरी तरह से अप्रासंगिक है। एफएक्यू पढ़ें, और शायद यह मेटा प्रश्न भी ।
ऑर्बिट में

11
@ TomalakGeret'kal बेशक आप अपनी राय व्यक्त कर सकते हैं और मैं आपकी टिप्पणी को असंवैधानिक होने के बावजूद सेंसर नहीं करूंगा। मैं पहले ही FAQ पढ़ चुका हूँ। मैं एक उत्साही प्रोग्रामर हूं, जो वास्तविक समस्या के
सेसर

जवाबों:


247

आपकी समस्या आपके पूर्वप्रक्रमक परिभाषाओं के साथ है INऔर OUT:

#define IN   1;     /* inside a word */
#define OUT  0;     /* outside a word */

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

    if (c == ' ' || c == '\n' || c == '\t')
        state = 0;; /* <--PROBLEM #1 */
    else if (state == 0;) { /* <--PROBLEM #2 */
        state = 1;;

उस दूसरे अर्धविराम के कारण मैच के रूप में elseकोई पिछली नहीं है if, क्योंकि आप ब्रेसिज़ का उपयोग नहीं कर रहे हैं। तो, के preprocessor परिभाषाओं से अर्धविराम निकालें INऔर OUT

यहां सीखा गया सबक यह है कि प्रीप्रोसेसर के बयान अर्धविराम के साथ समाप्त नहीं होते हैं।

इसके अलावा, आपको हमेशा ब्रेसिज़ का उपयोग करना चाहिए!

    if (c == ' ' || c == '\n' || c == '\t') {
        state = OUT;
    } else if (state == OUT) {
        state = IN;
        ++nw;
    }

elseउपरोक्त कोड में कोई लटका- अस्पष्टता नहीं है ।


8
स्पष्टता के लिए, समस्या रिक्ति नहीं है, यह अर्धविराम है। आप उन्हें पूर्व कथन में जरूरत नहीं है।
दान

@ स्पष्टीकरण के लिए धन्यवाद! और अर्धविराम वास्तव में समस्या थे! धन्यवाद दोस्तों!
सेसर

2
@ सेसर: आपका स्वागत है। ब्रेसिंग सुझाव उम्मीद है कि आप भविष्य में मुसीबत से बाहर रखेंगे, निश्चित रूप से मेरी मदद की है!
user7116

5
@ सेसर: मैक्रो के आसपास कोष्ठक लगाने की आदत डालना भी एक अच्छा विचार है क्योंकि आप चाहते हैं कि मैक्रो का पहले मूल्यांकन किया जाए। इस मामले में यह मायने नहीं रखता है क्योंकि मूल्य एक एकल टोकन है, लेकिन एक अभिव्यक्ति को परिभाषित करते समय परेंस को छोड़ने से अप्रत्याशित परिणाम हो सकते हैं।
स्टाइल डेफ

7
"उन्हें ज़रूरत नहीं है!" = "उन्हें नहीं होना चाहिए"। पूर्व हमेशा सत्य होता है; उत्तरार्द्ध संदर्भ-निर्भर है और इस परिदृश्य में अधिक प्रासंगिक मुद्दा है।
ऑर्बिट में

63

इस कोड के साथ मुख्य समस्या यह है कि यह K & R से कोड नहीं है। इसमें मैक्रो परिभाषाओं के बाद अर्धविराम शामिल हैं, जो पुस्तक में मौजूद नहीं थे, जैसा कि दूसरों ने बताया है कि इसका अर्थ बदल जाता है।

कोड को समझने की कोशिश में बदलाव करने के अलावा, आपको इसे तब तक अकेला छोड़ देना चाहिए जब तक आप इसे समझ नहीं लेते। आप केवल आपके द्वारा समझे गए कोड को सुरक्षित रूप से संशोधित कर सकते हैं।

यह शायद आपकी ओर से केवल एक टाइपो था, लेकिन यह प्रोग्रामिंग करते समय विवरणों को समझने और ध्यान देने की आवश्यकता को स्पष्ट करता है।


9
आपकी सलाह प्रोग्राम को सीखने वाले किसी व्यक्ति के लिए बहुत रचनात्मक नहीं है। संशोधन कोड ठीक है कि आप प्रोग्रामिंग के विवरण को कैसे समझते हैं।
user7116

12
@ सिक्सिटवर्वरियैबल्स: और ऐसा करते समय, आपको पता होना चाहिए कि आपने क्या बदलाव किए हैं, और कुछ बदलावों को संभव बनाएं। यदि ओपी ने जानबूझकर बदलाव किए हैं, और जितना संभव हो उतना कम बदलाव किया है, तो उन्होंने शायद यह सवाल नहीं पूछा होगा, क्योंकि यह उनके लिए स्पष्ट था कि क्या चल रहा था। उन्होंने IN के लिए मैक्रो को बदल दिया होगा, जिसमें कोई त्रुटि नहीं थी और फिर दो त्रुटियों के साथ OUT के लिए मैक्रो, जिनमें से दूसरा सेमीकोलन के बारे में शिकायत करेगा जो उसने अभी जोड़ा था।
जमरनो

5
ऐसा लगता है कि जब तक आप प्रीप्रोसेसर निर्देश पंक्ति के अंत में अर्धविराम सहित गलती नहीं करते हैं, तो आप संभवतः यह नहीं जान पाएंगे कि आप उन्हें शामिल नहीं कर रहे हैं। आप इसे अंकित मूल्य पर ले सकते हैं, आप बहुत सारे कोड पढ़ सकते हैं और नोटिस कर सकते हैं कि वे कभी नहीं लगते हैं। या, ओपी उन्हें शामिल करके गड़बड़ कर सकता है, "विचित्र" त्रुटि के बारे में पूछ सकता है, और पता लगा सकता है: उफ़, प्रीप्रोसेसर निर्देशों के लिए कोई अर्धविराम आवश्यक नहीं है! यह प्रोग्रामिंग है, न कि डरा हुआ सीधा का एक एपिसोड।
user7116

14
@sixlettervariables: हाँ, लेकिन जब कोड काम नहीं करता है, तो स्पष्ट रूप से पहला कदम "ओह, ठीक है, फिर मैंने बिना किसी कारण के जो भी बदल दिया, जो कि सी के आविष्कारक द्वारा एक किताब में लिखे गए कोड से शायद, शायद था मुद्दा। मैं बस उसके बाद पूर्ववत करूंगा। "
ऑर्बिट


34

मैक्रों के बाद कोई अर्धविराम नहीं होना चाहिए,

#define IN   1     /* inside a word */
#define OUT  0     /* outside a word */

और यह शायद होना चाहिए

if (c == ' ' || c == '\n' || c == '\t')

धन्यवाद, अर्धविराम समस्या थे। दूसरा एक टाइपो था!
सेसार

21
अगली बार कृपया अपने टेक्स्ट एडिटर से सीधे आपके द्वारा उपयोग किए जाने वाले सटीक कोड को पेस्ट करें ।
ऑर्बिट में

@ TomalakGeret'kal अच्छी तरह से मैं नहीं था और मैं करूँगा, लेकिन आपको कैसे मिला?
onemach

1
@onemach: आपने कहा कि ;एक टाइपो था जो समस्या को प्रभावित नहीं करता था, जिसका अर्थ है कि आपके प्रश्न में एक टाइपो कोड के बजाय जो आपने वास्तव में उपयोग किया था।
ऑर्बिट

24

IN और OUT की परिभाषा इस तरह दिखनी चाहिए:

#define IN   1     /* inside a word  */
#define OUT  0     /* outside a word */

अर्धविराम समस्या पैदा कर रहे थे! स्पष्टीकरण सरल है: IN और OUT दोनों प्रीप्रोसेसर निर्देश हैं, अनिवार्य रूप से कंपाइलर IN की सभी घटनाओं को 1 के साथ और OUT की सभी घटनाओं को स्रोत कोड में 0 से बदल देगा।

चूंकि मूल कोड में 1 और 0 के बाद अर्धविराम था, जब IN और OUT को कोड में बदल दिया गया था, संख्या के बाद अतिरिक्त अर्धविराम इस कोड के लिए अमान्य कोड का उत्पादन किया, उदाहरण के लिए:

else if (state == OUT)

इस तरह देख रहे हैं:

else if (state == 0;)

लेकिन आप क्या चाहते थे:

else if (state == 0)

समाधान: मूल परिभाषा में संख्याओं के बाद अर्धविराम निकालें।


8

जैसा कि आप देख रहे हैं कि मैक्रोज़ में एक समस्या थी।

प्री-प्रोसेसिंग के बाद जीसीसी के पास रुकने का विकल्प है (-ई) पूर्व प्रसंस्करण के परिणाम को देखने के लिए यह विकल्प उपयोगी है। यदि आप c / c ++ में बड़े कोड बेस के साथ काम कर रहे हैं तो वास्तव में तकनीक एक महत्वपूर्ण है। आमतौर पर पूर्व-प्रसंस्करण के बाद मेकफाइल्स को रोकने का लक्ष्य होगा।

त्वरित संदर्भ के लिए: SO प्रश्न में विकल्पों को शामिल किया गया है - मैं Visual Studio में प्रीप्रोसेसिंग के बाद C / C ++ स्रोत फ़ाइल कैसे देख सकता हूँ? । यह vc ++ से शुरू होता है, लेकिन इसमें नीचे दिए गए gcc विकल्प भी हैं


7

वास्तव में कोई समस्या नहीं है, लेकिन घोषणा की main()तिथि भी है, यह कुछ इस तरह होना चाहिए।

int main(int argc, char** argv) {
    ...
    return 0;
}

कंपाइलर एक फ़ंक्शन w / o एक के लिए एक इंटर्न रिटर्न मान ग्रहण करेगा, और मुझे यकीन है कि कंपाइलर / लिंकर argc / argv के लिए घोषणा की कमी और वापसी मूल्य की कमी के आसपास काम करेगा, लेकिन उन्हें वहां होना चाहिए।


3
यह एक अच्छी पुस्तक है - केवल दो ही मूल्य की, जबकि सी पर किताबें जहाँ तक मुझे पता है। मुझे पूरा यकीन है कि नए संस्करण ANSI C के अनुरूप हैं (शायद C99 ANSI C से पूर्व)। सी पर किताब के दूसरे मूल्य पीटर वैन डेर लिंडेन द्वारा विशेषज्ञ सी प्रोग्रामिंग डीप सी सीक्रेट हैं।
बिल

मैंने कभी नहीं कहा कि यह था। मुझे बस टिप्पणी की गई थी कि आज जिस तरह से चीजें की जाती हैं, उसके अनुरूप लाने के लिए, मुख्य को बदल दिया जाना चाहिए।
बिल

4

कोड ब्लॉकों के आसपास स्पष्ट ब्रेसिज़ जोड़ने का प्रयास करें। कश्मीर एंड आर शैली अस्पष्ट हो सकता है।

लाइन 18 को देखें। कंपाइलर आपको बता रहा है कि समस्या कहां है।

    if (c == '\n') {
        ++nl;
    }
    if (c == ' ' || c == '\n' || c == '\t') { // You're missing an "=" here; should be "=="
        state = OUT;
    }
    else if (state == OUT) {
        state = IN;
        ++nw;
    }

2
धन्यवाद! वास्तव में, कोड ने ब्रेसिज़ के बिना काम किया अगर :)
सेसर

5
+1। न सिर्फ अस्पष्ट बल्कि कुछ खतरनाक। जब (यदि) आप ifबाद में अपने ब्लॉक में एक पंक्ति जोड़ते हैं , तो यदि आप ब्रेसिज़ जोड़ना भूल जाते हैं क्योंकि आपका ब्लॉक अब एक पंक्ति से अधिक है, तो उस त्रुटि को डीबग करने में थोड़ा समय लग सकता है ...
The111

8
@ The111 कभी, कभी, मेरे साथ हुआ। मुझे अभी भी विश्वास नहीं है कि यह एक वास्तविक समस्या है। मैं एक दशक से ब्रेस-कम शैली का उपयोग कर रहा हूं, मैं एक बार ब्लॉक के शरीर का विस्तार करते समय ब्रेसिज़ जोड़ना कभी नहीं भूलता।
कोनराड रुडोल्फ

1
@ The111: इस मामले में कुछ ही मिनटों में कुछ मिनटों का समय लग जाता है: P और यदि आप एक प्रोग्रामर हैं, जो एक ifक्लॉज के लिए स्टेटमेंट जोड़ने में सक्षम है और ब्रेसेस को अपडेट करने के लिए "भूल" कर रहा है, तो ठीक है, आप नहीं हैं एक बहुत अच्छा प्रोग्रामर।
ऑर्बिट

3

एक सरल तरीका यह है कि प्रत्येक के लिए {} जैसे कोष्ठक का उपयोग करें ifऔर else:

if (c == '\n'){
    ++nl;
}
if (c == ' ' || c == '\n' || c == '\t')
{
    state = OUT;
}
else if (state == OUT) {
    state = IN;
    ++nw;
}

2

जैसा कि अन्य उत्तर में बताया गया है, समस्या #defineऔर अर्धविरामों में है। इन समस्याओं को कम करने के लिए, मैं हमेशा एक के रूप में नंबर स्थिरांक को परिभाषित करना पसंद करता हूं const int:

const int IN = 1;
const int OUT = 0;

इस तरह आप कई समस्याओं और संभावित समस्याओं से छुटकारा पा लेते हैं। यह सिर्फ दो चीजों द्वारा सीमित है:

  1. आपके कंपाइलर को समर्थन करना है const- जो कि 1988 में आम तौर पर सच नहीं था, लेकिन अब यह सभी आमतौर पर उपयोग किए जाने वाले कंपाइलरों द्वारा समर्थित है। (AFAIK constसी ++ से "उधार" है।)

  2. आप इन स्थिरांक का उपयोग कुछ विशेष स्थानों पर नहीं कर सकते हैं जहाँ आपको एक स्ट्रिंग-जैसे स्थिरांक की आवश्यकता होगी। लेकिन मुझे लगता है कि आपका कार्यक्रम ऐसा नहीं है।


एक विकल्प जो मुझे पसंद है वह है एनमोज़ - इनका उपयोग विशेष स्थानों (जैसे ऐरे घोषणाओं) में किया const intजा सकता है जो सी में नहीं हो सकते हैं
माइकल बूर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.