Reputation: 95
I'm trying to write a function which stores data in a text file, using the fopen("filename","w")
where "filename" is a string inputted by the user. I've chosen to do so via the getchar()
function and I need a counter variable that is being incremented with every keystroke. Here is where things get convoluted and confusing.
char *p;
int count = 0;
p = (char*)malloc(32*sizeof(char));
do
{
*p = getchar();
count++;
}
while(getchar() != '\n');
Up until 3 characters inputted it needs only 1 press of the Enter key and the counter is accurate until 2 characters after 2 it follows an odd pattern.
--------------------Input------------------------Count--------------------
-------------------- t -------------------------- 1 --------------------
-------------------- te ------------------------- 2 --------------------
-------------------- tes ----------------------- 2 --------------------
-------------------- test ------------------------ 3 ---------------------
-------------------- test1 ----------------------- 3 ---------------------
-------------------- test12 ---------------------- 4 ---------------------
-------------------- test123 --------------------- 4 ---------------------
-------------------- test1234 -------------------- 5 ---------------------
Basically, for every 2 additional chars, the count gets incremented by one.
How does the function works in this context and why does it need 2 keystrokes?
Upvotes: 0
Views: 159
Reputation: 1253
Dave Schwartz is right about calling getchar() twice. There are one or two more rough edges in your code fragment; let's talk about those while I expand a little bit on Dave's answer.
The C language can be VERY dense; multiple things can be going on in a single line of code. For someone just starting out, I would encourage you to write things long form, even if you don't think you need to... then consolidate as your mental model of what C is doing becomes richer.
For example... let's revise your code fragment to look like this:
int count = 0;
p = (char*)malloc(32*sizeof(char));
char c = getchar(); /* first call to getchar() */
while( c != '\n') {
p[count] = c; /* easier to read than *p=getchar() */
count++; /* could be combined... see below for a more C-like version. */
/* question: what would happen if we increment count BEFORE we store in p's memory? */
/* Also... your initial code was this: */
/* *p = getchar(); */
/* which is always assigning getchar to p[0]. */
/* see below for more "idiomatic" way to do that. */
/* see below for more "idiomatic" way to do that. */
/* Get the next char, then let while() condition decide if */
/* we come back into the loop body or proceed after it. */
/* It is a common tactic to put an input value in a scratch variable */
/* like 'c' and use it at different points in your loop. */
c = getchar(); /* all additional calls to getchar. */
/* note that we already declared c above the while-loop, so ok to re-use. */
}
/* dont want to call getchar() again here... this is the problem dave called out. */
/* while( getchar() != '\n' ); */
/* ...do some stuff here */
free(p); /* your logic does free the malloc at some point, yes? :-) */
Now you could try collapse as much code into a single expression as possible. But please don't do that - at least not until you can comfortably write code that is 1) easy to read, and 2) easy to predict what it will do.
You will eventually find writing code isn't the hard part.
It is reading code that was written 2 weeks (or 2+ years ago) that is hard.
Let's talk about looping next: while(), for() and do/while() to work with. The while() and for() give you the option to skip the loop body... the do/while() will Always execute the loop body, which may not be what you want in your situation since they user could just hit enter giving you the first character as '\n'.
for loop version Consider how your logic might look usuing a for() loop:
int count = 0;
p = (char*)malloc(32*sizeof(char));
for( char c = getchar(); c != '\n'; c = getchar() ) {
p[count] = c;
count++;
}
do/while version I find it more difficult to write this with a do/while() loop: Not that it is hard to write the code, but I find it hard to make the code readable so the intent is clear.
int count = 0;
p = (char*)malloc(32*sizeof(char));
char c;
do {
if( count >= 1 ) {
/* ugly... check count >= 1 so we don't save an uninitialized 'c' */
p[count] = c;
}
count++;
} while( (c = getchar() );
count--; /* adjust because we counted our '\n'. */
again with some extra C idioms This one is hard to read because to understand it you have to be able to untangle the while() expression, which does the following things:
1) calls the getchar() function and assigns the result to our temp var 'c'
2) compares the result of that assignment to '\n'
3) while() evaluates the result of the compare and only enters the loop body if we have something besides a '\n' in temp char var 'c'.
That is a lot to wrap your head around before you can ask "was it true?". fyi - this is the only sample I actually tried to run...
#include <stdio.h>
#include <malloc.h>
void main( char **argv ) {
printf("Please type some stuff:\n");
int count = 0;
char *p = (char*)malloc(32*sizeof(char));
char c;
while( (c = getchar() ) != '\n') {
p[count++] = c;
/* So... could we just saying the following instead?
* *p++ = c;
*/
}
p[count] = '\0'; /* this would turn our malloced buffer into a C-style string */
printf("found %d chars = '%s'\n", count, p);
free(p);
}
For one last point to ponder, consider the following:
1) Is there a limit to how many characters our user can type before they hit enter?
2) Assuming #1 does have a limit, what is it and why?
3) What could you do to add a check to ensure we don't let our user try to type too many characters?
Upvotes: 0
Reputation: 182761
You call getchar
twice each time in the loop. You push enter (newline) at the end of the characters. And you bump the count before the second getchar
. So the count will be (n + 1) / 2
rounded up, where n
is the number of characters.
Plus one for the newline. Divided by two because two characters are read for each increment. And rounded up because the increment occurs before the second read.
Upvotes: 6