Any way to take a word as input from stdin ?

B

Ben Bacarisse

Chad said:
Huh? On

size_t get_input( char** pword )

he passes &pword.

Yet, on

int get_single_word( char** ppc )

he passes pword.

Richard's point is that *in* get_input, pword is a char ** so it of
the right type to pass to get_single_word.

There are problems with this code, but a type miss-match is not one of
them.
 
B

Ben Bacarisse

arnuld said:
okay, here is the code which runs fine. DO you see any hidden bugs ?

It looks much the same as before. The loop from main has moved into
get_input. The type of get_input is not right for the final version
but I think you know that from Richard Heathfield's orginal comments.

<snip>
 
B

Barry Schwarz

okay, here is the code which runs fine. DO you see any hidden bugs ?

I find your definition of fine a little to broad.
/* A program that will ask the user for input and then will sort the input alphabetically
and will print it on stdout

* As of now it does not sort any words but it will very soon. I am writing it in parts
* when parts work fine,then we put them in one place :)

* Since I did not want to put any limitation on input size in this program, I have used
* dynamic memory allocation to solve the problem. My intent in creating and then solving
* this problem was purely of C learning (as defined by ANSI standard) and nothign else. I
* wanted to learn C and hence this problem and quite heavy discussion of it on comp.lang.c.
* I owe many thanks to the brillant help I got in comp.lang.c
*
* VERSION 1.0
*
*
*/


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>


enum { WORD_SIZE = 3 };
enum { GSW_OK, GSW_ENOMEM, GSW_ENORESIZE } ;


void print_input( char** );
size_t get_input( char** );
int get_single_word( char** );



int main( void )
{
char* pword;
size_t words_count;


words_count = get_input( &pword );

printf("words_count = %u\n", words_count);

words_count is unsigned but it need not be an int. If it is not, you
invoke undefined behavior here. This is one of the places in C89
where a cast is called for.
return 0;
}




void print_words( char **ppc )

I don't think you call this function.
{
printf("---------------------------------------\n\n");

while( **ppc )
{
printf("%c\n", **ppc++);

So you really want one character per line?

Since * and ++ have the same precedence and associate right to left,
the second argument is parsed as (*(*(ppc++))). It is your local copy
of ppc that is incremented. Since it started out pointing to pword in
main, it will now point one byte beyond pword. Since this byte is not
the start of a char*, the next time you dereference ppc you invoke
undefined behavior.

And you don't want to use *(*ppc)++ because that will increment pword.
You really need a local pointer if you want this syntax. I think a
local subscript would be easier to understand.
int i;
for (i = 0; (*ppc); i++)
printf(...
}
}




size_t get_input( char** pword )
{
size_t w_cnt;

w_cnt = 0;
while( (GSW_OK == get_single_word(pword)) && ( **pword != 0) )
{
printf("You entered: [%s]\n", *pword);
++w_cnt;
free( *pword );
}


return w_cnt;
}




int get_single_word( char** ppc )
{
unsigned ele_num;
int ch;
size_t word_length, word_length_interval;
char* word_begin;
char* new_mem;

ele_num = 0;

word_length = WORD_SIZE;
word_length_interval = 2;

*ppc = malloc(word_length * sizeof(**ppc));
word_begin = *ppc;


if( NULL == ppc )

If this if could ever evaluate to true then the previous statement
would have invoked undefined behavior first. But since ppc is
guaranteed to point to pword in main, this whole section is just dead
code that cannot be executed. You apparently meant *ppc.
{
return GSW_ENOMEM;

}


while( (EOF != (ch = getchar())) && isspace(ch) )
{
continue; /* Leading whitespace */
}


if( EOF != ch )
{
*word_begin++ = ch;
ele_num = 1;
}


while( (EOF != (ch = getchar())) && (! isspace(ch)) )
{
if( (word_length - 1) == ele_num++ )
{
new_mem = realloc( *ppc, (word_length_interval * word_length * sizeof *new_mem) );

if( new_mem )
{
word_begin = new_mem + (word_begin - *ppc);
word_length *= word_length_interval;
*ppc = new_mem;
}
else
{
*word_begin = '\0';
return GSW_ENORESIZE;
}
}

*word_begin++ = ch;
}


*word_begin = '\0';

return GSW_OK;

It is a small point but you do not distinguish between end of file and
an error on input.
 
A

arnuld

It looks much the same as before. The loop from main has moved into
get_input. The type of get_input is not right for the final version
but I think you know that from Richard Heathfield's orginal comments.

Oh.. I missed that. I have changed get_input from:

size_t get_input( char** )

to

int get_input( char**, size_t* )



but I get this problem then:


enum { GI_OK, GI_ERR };

int get_input( char** pword, size_t* w_cnt )
{
*w_cnt = 0;

while( (GSW_OK == get_single_word(pword)) && ( **pword != 0) )
{
printf("You entered: [%s]\n", *pword);
++*w_cnt;
free( *pword );
}

return GI_OK;
}



Any error in get_input() will come from what get_single_word() returns. If
I get an error, I don't understand how to catch it, it means I have to
change the while loop a a little:


int get_input( char** pword, size_t* w_cnt )
{
int err_catcher;
*w_cnt = 0;


while( (err_catcher = get_single_word(pword)) && ( **pword != 0) )
{
if( GSW_OK == err_catcher )
{
printf("You entered: [%s]\n", *pword);
++*w_cnt;
free( *pword );
}
else
{
return GI_ERR;
}
}

return GI_OK;
}
 
A

arnuld

words_count is unsigned but it need not be an int. If it is not, you
invoke undefined behavior here. This is one of the places in C89
where a cast is called for.

Now thats a big problem. words_count is size_t but you have no format
specifier for size_t. If i cast it to unsigned then K&R2 on page 204,
Appendix 7, 4.8) says the the particular type of size_t is unsigned
integer but that is implementation defined. Then why not make words_count
an unsigned int ?




I don't think you call this function.

yes, I did not because it will come much later (or may be I will remove
it, depends on when I will start implementing the final stage. final stage
will come after I will have sorted the input)


Since * and ++ have the same precedence and associate right to left, the
second argument is parsed as (*(*(ppc++))). It is your local copy of
ppc that is incremented. Since it started out pointing to pword in
main, it will now point one byte beyond pword. Since this byte is not
the start of a char*, the next time you dereference ppc you invoke
undefined behavior.


Thanks for the insight :)



It is a small point but you do not distinguish between end of file and
an error on input.

EOF mean Ctrl-D in Linux. What else could be the errors ?
 
B

Ben Bacarisse

arnuld said:
Now thats a big problem. words_count is size_t but you have no format
specifier for size_t.

C99 does: %zu and I thought you were permitting your self C99 extras.
In C90 the canonical solution is to cast to unsigned long -- size_t
can't be any "bigger" than unsigned long in C90 (it could be in C99).

EOF mean Ctrl-D in Linux. What else could be the errors ?

An unreadable file? Someone pulls the plug? Loads of things can go
wrong reading a file. You don't *have* to report a read error (Barry
says it is a small point) but you might want to.
 
B

Ben Bacarisse

arnuld said:
Oh.. I missed that. I have changed get_input from:

size_t get_input( char** )

to

int get_input( char**, size_t* )

I thought tha plan was that get_input would set its parameter to a
char ** containing the list of words? If so, it needs to be a char
*** but I see you are currently just printing words inside get_input.
If that is the new design, the call it print_words or some such.
but I get this problem then:


enum { GI_OK, GI_ERR };

<snip code>
I won't comment on the details until I know what the intent is.
 
A

arnuld

C99 does: %zu and I thought you were permitting your self C99 extras.
In C90 the canonical solution is to cast to unsigned long -- size_t
can't be any "bigger" than unsigned long in C90 (it could be in C99).

I was going for C99 but then many people here can't compile C99 based
programs. Thats why I am working on C90 based solutions

An unreadable file? Someone pulls the plug? Loads of things can go
wrong reading a file. You don't *have* to report a read error (Barry
says it is a small point) but you might want to.


But how you will catch the error. You catch End of File by using EOF
macro. What can I use to catch different errors you mentioned ?
 
A

arnuld

I thought tha plan was that get_input would set its parameter to a
char ** containing the list of words? If so, it needs to be a char
*** but I see you are currently just printing words inside get_input.
If that is the new design, the call it print_words or some such.


No, I plan to sort them but don't know how. Right now I am just printing
them and free()ing the memory after every single word is printed

But if I need to sort them, then first I need to collect them all and only
then I could free(), that may create memory run-out problem. You have
better ideas for alphabetical sorting ?


I won't comment on the details until I know what the intent is.


The intent is to take input from stdin with the definition of words we
have agreed about some time ago. Then sorting those words alphbetically
and printing them on the stdout. Thats it. All must be done using Dynamic
Memory Allocation. I am using this exercise a way to teach myself C and
general programming concepts.
 
J

James Kuyper

arnuld said:
But how you will catch the error. You catch End of File by using EOF
macro. What can I use to catch different errors you mentioned ?

The same way. getchar() uses EOF to signal any problems it could run
into. If you need to distinguish between an End of File and an I/O
error, you have to use feof() and ferror().
 
A

arnuld

feof() and ferror().

I checked man pages, both take FILE* as input. we are dealing with stdin
here. Even if it is feasible, then I have to make very massive changes in
my program.
 
B

Ben Bacarisse

arnuld said:
No, I plan to sort them but don't know how. Right now I am just printing
them and free()ing the memory after every single word is printed

But if I need to sort them, then first I need to collect them all and only
then I could free(), that may create memory run-out problem. You have
better ideas for alphabetical sorting ?





The intent is to take input from stdin with the definition of words we
have agreed about some time ago. Then sorting those words alphbetically
and printing them on the stdout. Thats it. All must be done using Dynamic
Memory Allocation. I am using this exercise a way to teach myself C and
general programming concepts.

Then, as a learning exercise, I'd implement get_input (I'd probably
call it get_words but that is not important) and sort the array and
print it "outside" (probably in main). That gives you more learning!
 
A

arnuld

Then, as a learning exercise, I'd implement get_input (I'd probably
call it get_words but that is not important) and sort the array and
print it "outside" (probably in main). That gives you more learning!

well ,thats what I am doing. I don't see any difference in your idea. I
even have a function name i mind: printf_input(..) and also I will free()
in main() too, as input char* to get_input(...) is also created in main().
 
B

Ben Bacarisse

arnuld said:
well ,thats what I am doing. I don't see any difference in your idea. I
even have a function name i mind: printf_input(..) and also I will free()
in main() too, as input char* to get_input(...) is also created in
main().

Great. Post an outline. What you posted before had the wrong types
for that sort of design.
 
R

Richard Tobin

feof() and ferror().

I checked man pages, both take FILE* as input. we are dealing with stdin
here. Even if it is feasible, then I have to make very massive changes in
my program.[/QUOTE]

Why? stdin is a FILE *.

-- Richard
 
C

Chad

okay, here is the code which runs fine. DO you see any hidden bugs ?

I find your definition of fine a little to broad.




/* A program that will ask the user for input and then will sort the input alphabetically
and will print it on stdout
* As of now it does not sort any words but it will very soon. I am writing it in parts
* when parts work fine,then we put them in one place :)
* Since I did not want to put any limitation on input size in this program, I have used
* dynamic memory allocation to solve the problem. My intent in creating and then solving
* this problem was purely of C learning (as defined by ANSI standard) and nothign else. I
* wanted to learn C and hence this problem and quite heavy discussion of it on comp.lang.c.
* I owe many thanks to the brillant help I got in comp.lang.c
*
* VERSION 1.0
*
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
enum { WORD_SIZE = 3 };
enum { GSW_OK, GSW_ENOMEM, GSW_ENORESIZE } ;
void print_input( char** );
size_t get_input( char** );
int get_single_word( char** );
int main( void )
{
char* pword;
size_t words_count;
words_count = get_input( &pword );
printf("words_count = %u\n", words_count);

words_count is unsigned but it need not be an int. If it is not, you
invoke undefined behavior here. This is one of the places in C89
where a cast is called for.


return 0;
}
void print_words( char **ppc )

I don't think you call this function.
{
printf("---------------------------------------\n\n");
while( **ppc )
{
printf("%c\n", **ppc++);

So you really want one character per line?

Since * and ++ have the same precedence and associate right to left,
the second argument is parsed as (*(*(ppc++))). It is your local copy
of ppc that is incremented. Since it started out pointing to pword in
main, it will now point one byte beyond pword. Since this byte is not
the start of a char*, the next time you dereference ppc you invoke
undefined behavior.

And you don't want to use *(*ppc)++ because that will increment pword.
You really need a local pointer if you want this syntax. I think a
local subscript would be easier to understand.
int i;
for (i = 0; (*ppc); i++)
printf(...


Why would it matter if pword gets incremented?
 
B

Barry Schwarz

Now thats a big problem. words_count is size_t but you have no format
specifier for size_t. If i cast it to unsigned then K&R2 on page 204,
Appendix 7, 4.8) says the the particular type of size_t is unsigned
integer but that is implementation defined. Then why not make words_count
an unsigned int ?

You can if you want to.

EOF mean Ctrl-D in Linux. What else could be the errors ?

Doesn't Linux support the < redirection operator so that stdin could
be a file? Could not whatever device the stream connects to
(including the keyboard) suffer a hardware problem? Can the cable
that connects the device to the computer break or accidentally be
pulled out of its receptacle? Could the driver which accesses the
device be coded improperly or be corrupted in memory? For a disk
file, could the file system be corrupt and point to a non-existent
sector on disk? Could the disk be formatted incorrectly? Could
another process attempt to format the disk (or delete the file) while
you are reading from it?
 
C

Chad

I find your definition of fine a little to broad.
words_count is unsigned but it need not be an int. If it is not, you
invoke undefined behavior here. This is one of the places in C89
where a cast is called for.
I don't think you call this function.
So you really want one character per line?
Since * and ++ have the same precedence and associate right to left,
the second argument is parsed as (*(*(ppc++))). It is your local copy
of ppc that is incremented. Since it started out pointing to pword in
main, it will now point one byte beyond pword. Since this byte is not
the start of a char*, the next time you dereference ppc you invoke
undefined behavior.
And you don't want to use *(*ppc)++ because that will increment pword.
You really need a local pointer if you want this syntax. I think a
local subscript would be easier to understand.
int i;
for (i = 0; (*ppc); i++)
printf(...


Why would it matter if pword gets incremented?



Wait. I think I see why.

printf("%c\n", **ppc++);

is the same as


printf("%c\n", (*ppc++)[0]);

And the OP wants to increment each word, not increment each letter in
a word. I think. Okay, I'm done talking to myself. With that, I think
I'm going to drive to Denny's in Richmond and just talk to myself.
 
A

arnuld

Great. Post an outline. What you posted before had the wrong types
for that sort of design.


Here is the outline which gives run time error of the free() call in main():




/* A program that will ask the user for input and then will sort the input alphabetically
and will print it on stdout

* As of now it does not sort any words but it will very soon. I am writing it in parts
* when parts work fine,then we put them in one place :)

* Since I did not want to put any limitation on input size in this program, I have used
* dynamic memory allocation to solve the problem. My intent in creating and then solving
* this problem was purely of C learning (as defined by ANSI standard) and nothing else. I
* wanted to learn C and hence this problem and quite heavy discussion of it on comp.lang.c.
* I owe many thanks to the brilliant help I got in comp.lang.c
*
* VERSION 1.0
*
*
*/


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>


enum { WORD_SIZE = 3, WORD_ERR = -1 };
enum { GSW_OK, GSW_ENOMEM, GSW_ENORESIZE } ;
enum { GI_OK = 0 , GI_ERR = WORD_ERR };


int get_input( char**, size_t* );
int get_single_word( char** );



int main( void )
{
char* pword;
int input_err;
size_t words_count;

pword = NULL;
words_count = 0;
input_err = WORD_ERR; /* we will update the status in function call to get_input() */

input_err = get_input( &pword, &words_count );

if( WORD_ERR != input_err )
{
printf("words_count = %u\n", words_count);
}


/* sort_input(...)
printf_input(...) */

free( &pword );


return 0;
}



int get_input( char** pword, size_t* w_cnt )
{
int err_catcher;
*w_cnt = 0;


while( (err_catcher = get_single_word(pword)) && ( **pword != 0) )
{
if( GSW_OK == err_catcher )
{
printf("You entered: [%s]\n", *pword);
++*w_cnt;
}
else
{
return GI_ERR;
}
}

return GI_OK;
}




int get_single_word( char** ppc )
{
unsigned ele_num;
int ch;
size_t word_length, word_length_interval;
char* word_begin;
char* new_mem;

ele_num = 0;

word_length = WORD_SIZE;
word_length_interval = 2;

*ppc = malloc(word_length * sizeof(**ppc));
word_begin = *ppc;


if( NULL == word_begin )
{
return GSW_ENOMEM;

}


while( (EOF != (ch = getchar())) && isspace(ch) )
{
continue; /* Leading whitespace */
}


if( EOF != ch )
{
*word_begin++ = ch;
ele_num = 1;
}


while( (EOF != (ch = getchar())) && (! isspace(ch)) )
{
if( (word_length - 1) == ele_num++ )
{
new_mem = realloc( *ppc, (word_length_interval * word_length * sizeof *new_mem) );

if( new_mem )
{
word_begin = new_mem + (word_begin - *ppc);
word_length *= word_length_interval;
*ppc = new_mem;
}
else
{
*word_begin = '\0';
return GSW_ENORESIZE;
}
}

*word_begin++ = ch;
}


*word_begin = '\0';

return GSW_OK;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,240
Members
46,828
Latest member
LauraCastr

Latest Threads

Top