tags:

views:

185

answers:

5

Hi all,

I bought a C book called "The C (ANSI C) PROGRAMMING LANGUAGE" to try and teach myself, well C. Anyhow, the book includes a lot of examples and practices to follow across the chapters, which is nice.

Anyhow, the code below is my answer to the books "count the longest line type of program", the authors are using a for-loop in the function getLine(char s[], int lim). Which allows for a proper display of the string line inside the main() function. However using while won't work - for a reason that is for me unknown, perhaps someone might shed a light on the situation to what my error is.

EDIT: To summarize the above. printf("%s\n", line); won't display anything.

Thankful for any help.

#include <stdio.h>
#define MAXLINE 1024

getLine(char s[], int lim) {
    int c, i = 0;

    while((c = getchar()) != EOF && c != '\n' && i < lim) {
        s[++i] = c;
    }

    if(c == '\n' && i != 0) {
        s[++i] = c;
        s[++i] = '\0';
    }    
    return i;
}  

main(void) {
    int max = 0, len;
    char line[MAXLINE], longest[MAXLINE];

    while((len = getLine(line,MAXLINE)) > 0) {
        if(len > max) {
            max = len;
            printf("%s\n", line);
        }
    }
    return 0;
}
+1  A: 

You have a number of serious bugs. Here's the ones I found and how to fix them.

change your code to postincrement i to avoid leaving the first array member uninitialised, and to avoid double printing the final character:

s[++i] = c;
...
s[++i] = c;
s[++i] = '\0';

to

s[i++] = c;
...
// s[++i] = c; see below
...
s[i++] = '\0'; 

and fix your EOF bug:

if(c == '\n' && i != 0) {
    s[++i] = c;
    s[++i] = '\0';
}

to

if(c == '\n')
{
    s[i++] = '\n';
}
s[i] = '\0'

Theory

When writing programs that deal with strings, arrays or other vector-type structures it is vitally important that you check the logic of your program. You should do this by hand, and run a few sample cases through it, providing sample inputs to your program and thinking out what happens.

The cases you need to run through it are:

  • a couple general cases
  • all the edge cases

In this case, your edge cases are:

  • first character ever is EOF
  • first character is 'x', second character ever is EOF
  • first character is '\n', second character is EOF
  • first character is 'x', second character is '\n', third character is EOF

  • a line has equal to lim characters

  • a line has one less than lim characters
  • a line has one more than lim characters

Sample edge case

first character is 'x', second character is '\n', third character is EOF

getLine(line[MAXLINE],MAXLINE])
(s := line[MAXLINE] = '!!!!!!!!!!!!!!!!!!!!!!!!...'
c := undef, i := 0
while(...)
c := 'x'
i := 1
s[1] := 'x' => s == '!x!!!!...' <- first bug found
while(...)
c := '\n'
end of while(...)
if (...)
(c== '\n' (T) && i != 0 (T)) = T
i := i + 1 = 2
s[2] = '\n' => s == '!x\n!!!!'
i := i + 1 = 3
s[3] = '\0' => s == '!x\n\0!!!' <- good, it's terminated
return i = 3
(len = i = 3) > 0) = T (the while eval)
if (...)
len (i = 3) > max = F
max = 3 <- does this make sense?  is '!x\n' a line 3 chars long?  perhaps.  why did we keep the '\n' character? this is likely to be another bug.
printf("%s\n", line) <- oh, we are adding ANOTHER \n character?  it was definitely a bug.
outputs "!x\n\n\0" <- oh, I expected it to print "x\n".  we know why it didn't.
while(...)
getLine(...)
(s := line[MAXLINE] = '!x\n\0!!!!!!!!!!!!!!!!!!!...' ; <- oh, that's fun.
c := undef, i := 0
while(...)
c := EOF
while terminates without executing body
(c == '\n' && i != 0) = F
if body not executed
return i = 0
(len = i = 0 > 0) = F
while terminates
program stops.

So you see this simple process, that can be done in your head or (preferably) on paper, can show you in a matter of minutes whether your program will work or not.

By following through the other edge cases and a couple general cases you will discover the other problems in your program.

Alex Brown
note that this code will count \n in i. you can fix that yourself.
Alex Brown
Thanks for the help (and patience), i really appreciate it.
Sven
Acknowledging your edit above - it's very helpful and good practice - however this is merely the first chapter, which has not yet touched this subject quite deeply yet.
Sven
However - as an advice - even when writing these sorts of practice applications inside a book. Do you recommend to write for the "extreme edge cases"?
Sven
I don't know what you mean by extreme edge... isn't an edge extreme by definition? or do you mean very very unlikely edge cases? none of the edge cases I listed are very unlikely.
Alex Brown
Let me rephrase the question - should i make it a habit (despite the book not mentioning it, so far) to code for cases - such as the ones above?
Sven
A: 

It's not clear from your question exactly what problem you're having with getLine (compile error? runtime error?), but there are a couple of bugs in your implementation. Instead of

s[++i] = something;

You should be using the postfix operator:

s[i++] = something;

The difference is that the first version stores 'something' at the index of (i+1), but the second version will store something at the index of i. In C/C++, arrays are indexed from 0, so you need to make sure it stores the character in s[0] on the first pass through your while loop, in s[1] on the second pass through, and so on. With the code you posted, s[0] is never assigned to, which will cause the printf() to print out unintialised data.

The following implementation of getline works for me:

int getLine(char s[], int lim) {

    int c;
    int i;

    i = 0;

    while((c = getchar()) != EOF && c != '\n' && i < lim) {
        s[i++] = c;
    }

    if(c == '\n' && i != 0) {
        s[i++] = c;
        s[i++] = '\0';
    }

    return i;
}
HulkHolden
Alex Brown
A: 

By doing ++i instead of i++, you are not assigning anything to s[0] in getLine()!

Also, you are unnecesarilly incrementing when assigning '\0' at the end of the loop, which BTW you should always assign, so take it out from the conditional.

Ariel
Thanks for this piece of information.
Sven
A: 

Also add return types to the functions (int main and int getLine)

George B.
A: 

Watch out for the overflow as well - you are assigning to s[i] at the end with a limit of i == lim thus you may be assigning to s[MAXLINE]. This would be a - wait for it - stack overflow, yup.

Nicolas Galler