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)
|