Weird problem

  • Follow


Hello everybody,

I was doing one of the exercises in the K&R book, and I got something
really strange. Here's the source code:

/*
 * Exercise 2-2 from the K&R book, page 42
*/
#include <stdio.h>

enum loop_control { EXIT, CONTINUE };

int getline (char s[], int lim)
{
    short int loop = CONTINUE;
    unsigned int i = 0;
    int c; 

    while (loop) {
        c = getchar();
        if (i == (lim - 2))
            loop = EXIT;
        else if (c == EOF)
            loop = EXIT;
        else if (c == '\n')
            loop = EXIT;
        else
            s[i++] = c;
    }

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

    return (i);
}

The strange thing is that lim doesn't seem to control anything at all.
I compiled it using GCC under cygwin on a windows 98 system (I added a
simple main() function that gets a line using getline() and then
prints it).
The strange thing is that even when I put a very small value for lim,
say 10, I can still get very big strings into it without it crashing.
Here's an example output:
$ gcc -o ex exercise2-2.c
$ ./ex
123333333333333333
1233333333333333
fdkjghfgsfgkjsdf
fdkjghfgfgkjsdf
(^C)
$ 

Any feedback will be appreciated!
Cheers!

- Joseph
0
Reply agenthunt (11) 11/7/2003 12:31:20 PM


On 11/7/2003 6:31 AM, Jeff wrote:
> Hello everybody,
> 
> I was doing one of the exercises in the K&R book, and I got something
> really strange. Here's the source code:
<snip>
> The strange thing is that lim doesn't seem to control anything at all.
> I compiled it using GCC under cygwin on a windows 98 system (I added a
> simple main() function that gets a line using getline() and then
> prints it).
> The strange thing is that even when I put a very small value for lim,
> say 10, I can still get very big strings into it without it crashing.
> Here's an example output:
> $ gcc -o ex exercise2-2.c
> $ ./ex
> 123333333333333333
> 1233333333333333
> fdkjghfgsfgkjsdf
> fdkjghfgfgkjsdf

Notice that the above string is correct for the first 8 chars (i.e. lim -2 when
lim is 10) then it skips the "s" and keeps going. You didn't by any chance write
your main() function as:

int main() {
	...
	while(1) {
		getline(s,10);
		printf("%s",s);
	}
	...
}

did you? I suspect you're calling getline() in a loop. Show us your "main()" if
that isn't the case.

	Ed.

> (^C)
> $ 
> 
> Any feedback will be appreciated!
> Cheers!
> 
> - Joseph

0
Reply mortonAVOIDINGSPAM (167) 11/7/2003 1:06:23 PM


agenthunt@netcourrier.com (Jeff) wrote:

> I was doing one of the exercises in the K&R book, and I got something
> really strange. Here's the source code:
 
> #include <stdio.h>
> 
> enum loop_control { EXIT, CONTINUE };
> 
> int getline (char s[], int lim)
> {
>     short int loop = CONTINUE;
>     unsigned int i = 0;
>     int c; 
> 
>     while (loop) {
>         c = getchar();
>         if (i == (lim - 2))

What if this function is invoked with second parameter < 2 ?

The buffer s isn't used as efficiently as possible, if there's 
no '\n' within the first lim-2 characters of input.

>             loop = EXIT;
>         else if (c == EOF)
>             loop = EXIT;
>         else if (c == '\n')
>             loop = EXIT;
>         else
>             s[i++] = c;
>     }
> 
>     if (c == '\n')
>         s[i++] = c;
>     s[i] = '\0';
> 
>     return (i);

You return an unsigned int from a function declared as returning int. 
BTW, the parentheses in the return statement are unnecessary.

> }
> 
> The strange thing is that lim doesn't seem to control anything at all.
> I compiled it using GCC under cygwin on a windows 98 system (I added a
> simple main() function that gets a line using getline() and then
> prints it).
> The strange thing is that even when I put a very small value for lim,
> say 10, I can still get very big strings into it without it crashing.
> Here's an example output:
> $ gcc -o ex exercise2-2.c
> $ ./ex
> 123333333333333333
> 1233333333333333
> fdkjghfgsfgkjsdf
> fdkjghfgfgkjsdf

I cannot reproduce this behaviour.  Here's how I used your function:
 
#define BUFLEN 10

int main( void )
{
    char str[BUFLEN];
    int n = getline( str, BUFLEN );

    printf("%d %s\n", n, str );
    return 0;
}

Would you mind to post your main function as well?  Maybe something's 
wrong with it.

Anyway, how about this:

int getline( char *s, int lim )
{
    int i = 0;
    int c;

    while( ((c = getchar()) != EOF) && (c != '\n') && (i < lim-2) )
        s[i++] = c;
    if (c == '\n')
        s[i++] = c;
    s[i] = '\0';

    return i;
}

Note: this version still uses the buffer provided by the caller
inefficiently, if no '\n' appears within the first lim-2 characters 
of input.  You may want to improve it (left as an exercise).

Regards
-- 
Irrwahn 
(irrwahn33@freenet.de)
0
Reply irrwahn33 (608) 11/7/2003 1:48:30 PM

On 7 Nov 2003 04:31:20 -0800
agenthunt@netcourrier.com (Jeff) wrote:

> Hello everybody,
> 
> I was doing one of the exercises in the K&R book, and I got something
> really strange. Here's the source code:
> 
> /*
>  * Exercise 2-2 from the K&R book, page 42
> */
> #include <stdio.h>
> 
> enum loop_control { EXIT, CONTINUE };
> 
> int getline (char s[], int lim)

size_t would be better than int, look up size_t in K&R.

> {
>     short int loop = CONTINUE;

I would suggest
      enum loop_control loop = CONTINUE;

A good debugger might then show you the enumeration used. If memory was
tight and you wanted to save space then you could make loop a char, but
I can't see any reason for a short in this case.

>     unsigned int i = 0;

Again, size_t would be a better type.

>     int c; 
> 
>     while (loop) {
>         c = getchar();
>         if (i == (lim - 2))

You have a problem if lim == 1.

>             loop = EXIT;
>         else if (c == EOF)
>             loop = EXIT;
>         else if (c == '\n')
>             loop = EXIT;

Why two seperate ifs?

>         else
>             s[i++] = c;
>     }
> 
>     if (c == '\n')
>         s[i++] = c;

You could have handled this when you detected the newline above. Also,
if you hit the lim-2 without a newline you have just thrown away a
character.

>     s[i] = '\0';

A problem if lim==1, even if that is a silly value.

> 
>     return (i);
> }
> 
> The strange thing is that lim doesn't seem to control anything at all.
> I compiled it using GCC under cygwin on a windows 98 system (I added a
> simple main() function that gets a line using getline() and then
> prints it).
> The strange thing is that even when I put a very small value for lim,
> say 10, I can still get very big strings into it without it crashing.
> Here's an example output:
> $ gcc -o ex exercise2-2.c
> $ ./ex
> 123333333333333333
> 1233333333333333
> fdkjghfgsfgkjsdf
> fdkjghfgfgkjsdf

If you used the following main you would get a better idea.


#define SIZ 5

int main(void)
{
    char buf[SIZ];
    int len;
    while (1) {
       len = getline(buf,SIZ);
       printf("\"%s\" %d\n",buf,len);
    }
    return 0;
}

Here is a possible rework of your code. Note, I've reworked your code
rather than trying to solve the exersize, so it may not be what you
actually want.

int getline (char s[], size_t lim)
{
    enum loop_control loop = CONTINUE;
    size_t i = 0;
    int c; 

    while (loop) {
        c = getchar();
        if (c == EOF)
            loop = EXIT;
        else {
            s[i++] = c;
	    if (c == '\n' || i+1 >= lim)
                loop = EXIT;
	}
    }

    if (i<lim)
        s[i] = '\0';

    return (i);
}

HTH.
-- 
Mark Gordon
Paid to be a Geek & a Senior Software Developer
Although my email address says spamtrap, it is real and I read it.
0
Reply spamtrap606 (150) 11/7/2003 3:50:41 PM

> Anyway, how about this:
> 
> int getline( char *s, int lim )
> {
>     int i = 0;
>     int c;
> 
>     while( ((c = getchar()) != EOF) && (c != '\n') && (i < lim-2) )
>         s[i++] = c;
>     if (c == '\n')
>         s[i++] = c;
>     s[i] = '\0';
> 
>     return i;
> }

That's the actual function, the exercise is to re-write it without
using &&'s ;)

I can't really remember what my main () function was exactly, but it
was basically something like this:

#define MAXLENGTH 10

int main (void)
{
    int len;
    char s[MAXLENGTH];

    while ((len = getline (s, MAXLENGTH)) > 0)
        printf("%s");

    return (0);
}

Oh, and BTW I know the parentheses are unecessary, but I just like
writing my code like that.

Thanks for the feedback,

- Joseph
0
Reply agenthunt (11) 11/7/2003 7:06:02 PM

Oups, sorry about the error, it should be printf ("%s", s);

- Joseph
0
Reply agenthunt (11) 11/7/2003 7:12:30 PM


On 11/7/2003 1:06 PM, Jeff wrote:
<snip>
> I can't really remember what my main () function was exactly, but it
> was basically something like this:
> 
> #define MAXLENGTH 10
> 
> int main (void)
> {
>     int len;
>     char s[MAXLENGTH];
> 
>     while ((len = getline (s, MAXLENGTH)) > 0)
>         printf("%s");

Change that to:

	printf("[%s]",s);
	
then rerun your test and the source of your problem should become clear.

	Ed.

0
Reply mortonAVOIDINGSPAM (167) 11/7/2003 7:43:39 PM

agenthunt@netcourrier.com (Jeff) wrote:

[attribution restored, please do not snip it, thank you]

> Irrwahn Grausewitz wrote:
> > Anyway, how about this:
> > 
> > int getline( char *s, int lim )
> > {
> >     int i = 0;
> >     int c;
> > 
> >     while( ((c = getchar()) != EOF) && (c != '\n') && (i < lim-2) )
> >         s[i++] = c;
> >     if (c == '\n')
> >         s[i++] = c;
> >     s[i] = '\0';
> > 
> >     return i;
> > }
> 
> That's the actual function, the exercise is to re-write it without
> using &&'s ;)

Yikes, sorry, I didn't have my copy of K&R handy... :)

> I can't really remember what my main () function was exactly, but it
> was basically something like this:
> 
> #define MAXLENGTH 10
> 
> int main (void)
> {
>     int len;
>     char s[MAXLENGTH];
> 
>     while ((len = getline (s, MAXLENGTH)) > 0)
>         printf("%s");
OK, you already corrected this in another reply to read:

         printf ("%s", s);

Well, and now consider what happens:  you read up to MAXLENGTH-2
characters in the first call of getline, *but* the rest of the 
input is still waiting and gets processed in the consecutive calls 
to getline.  If you change the line to

         printf ("%s\n", s);

you'll see that getline works as expected, just your output looked 
like it didn't.

> 
>     return (0);
> }
> 
> Oh, and BTW I know the parentheses are unecessary, but I just like
> writing my code like that.

Well, I just thought I mention it for the sake of whatever...  :)
 
> Thanks for the feedback,

You're welcome.

Regards
-- 
Irrwahn 
(irrwahn33@freenet.de)
0
Reply irrwahn33 (608) 11/7/2003 8:35:46 PM

Jeff <agenthunt@netcourrier.com> wrote:
> Hello everybody,

Hello, Jeff,

> I was doing one of the exercises in the K&R book, and I got something
> really strange. Here's the source code:

> /*
>  * Exercise 2-2 from the K&R book, page 42
> */
> #include <stdio.h>

> enum loop_control { EXIT, CONTINUE };

> int getline (char s[], int lim)
> {
>     short int loop = CONTINUE;
>     unsigned int i = 0;
>     int c; 

>     while (loop) {
>         c = getchar();
>         if (i == (lim - 2))
>             loop = EXIT;
>         else if (c == EOF)
>             loop = EXIT;
>         else if (c == '\n')
>             loop = EXIT;
>         else
>             s[i++] = c;
>     }

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

>     return (i);
> }

> The strange thing is that lim doesn't seem to control anything at all.
> I compiled it using GCC under cygwin on a windows 98 system (I added a
> simple main() function that gets a line using getline() and then
> prints it).

Sure it does sometihng.
It leaves room in the buffer for the trailing \n and \0.
That's why you see the expression 'lim - 2'.


> The strange thing is that even when I put a very small value for lim,
> say 10, I can still get very big strings into it without it crashing.
> Here's an example output:
> $ gcc -o ex exercise2-2.c
> $ ./ex
> 123333333333333333
> 1233333333333333
> fdkjghfgsfgkjsdf
> fdkjghfgfgkjsdf
> (^C)
> $ 

This seems strange to me, because I ran it under cygwin under
Win2k and I got exactly what I expected.

Here's what I recommend, for your own edification:

Change the four parts of the if-else if-else if-else block to
contain a printf statement that states what's happening.

I did that, too, and here it is:

        if (i == (lim - 2))
        {
            loop = EXIT;
            printf ("Hit limit: exiting\n");
        }
        else if (c == EOF)
        {
            loop = EXIT;
            printf ("Hit EOF: exiting\n");
        }
        else if (c == '\n')
        {
            loop = EXIT;
            printf ("Hit EOL: exiting\n");
        }
        else
        {
            s[i++] = c;
            printf ("Adding character '%c'\n", c);
        }

I got exactly what I expected.

HTH

> Cheers!

> - Joseph

0
Reply mgessner1 (2) 11/12/2003 3:06:19 PM

8 Replies
58 Views

(page loaded in 0.167 seconds)

5/25/2013 7:27:09 PM


Reply: