![]() |
|
#1
|
||||
|
||||
Scanning dilemma (fscanf and fgets)hello,
I have been trying to write a simple refresher program which writes stuff into an array of structs and then tries to read that stuff out to the console. Ive used functions for both the tasks mentioned above Heres the code: CPP / C++ / C Code:
Heres the make file: Code:
I assume it has got to do something with the clearing of the input streams. What would be the best way to read streamed user input like this into different variables? I think I somehow need to take care of clearing the string so that it may be ready to accept new user input Additionally in both of the functions in the code above viz. fill_in and read, I was initially using a formal parameter of the type pointer to tag which I later changed to the type tag p[ ]. The previous pointer declaration was giving me errors whenever I was using the '->' operator to access various elements (to write user data to) in the fscanf and the fgets statements. eg. With tag *p as the formal parameter in function fill_in I was using it in the fscanf statement as: fscanf(stdin,"%s",p[i] -> fname); The compiler was then flagging me a type mismatch error withn regards to 'p'. It said: error: invalid type argument of `->' What is the difference between declaring the formal parameter to be an unconstrained array (tag p[ ]) and as a pointer (tag *p)? And how exactly was it affecting its use in fscanf/fgets Hope to hear from you guys, Best Regards, Aijaz __________________
Hope to hear from you guys! -------------------------------------------------- Best Regards, Aijaz Baig. |
||||
|
#2
|
|||||||
|
|||||||
Re: the scanning dilemma (fscanf and fgets)Quote:
Change it to use three instead of ten inputs, and show us what happened in the program. Just paste the output into your post. For example, with that change and no other changes in your code: Code:
Quote:
Quote:
Quote:
Quote:
Well, if p is a pointer to a struct, then you can use p->fname, to access the element that p is pointing to. However p[ i ] will be a struct, so you use p[ i ].fname to access the element that (p+i) is pointing to. Note that the notation will be the same whether the parameter is declared as tag * or tag[]. Quote:
Quote:
Regards, Dave Footnote: One very fundamental concept: If p is a pointer data type and j is an integer data type, the following are taken to be identical by C compilers (C++ too) CPP / C++ / C Code:
is identical to CPP / C++ / C Code:
Note that an array name is treated by the compiler as a constant pointer whose value is the address of the first element of the array, so the above two expressions are also valid and the effects are identical if p is the name of an array. Note that i am not (that's not) saying that arrays are the same thing as pointers. They are not. It's just notation. Really. Now, consider the case where p is a pointer to a struct (notation will be the same if p is an array of structs): CPP / C++ / C Code:
is the same as CPP / C++ / C Code:
And that's the same as CPP / C++ / C Code:
In particular: CPP / C++ / C Code:
is the same as CPP / C++ / C Code:
and that is the same as CPP / C++ / C Code:
Last edited by davekw7x : 25-Mar-2008 at 08:56.
|
|
#3
|
||||
|
||||
Re: the scanning dilemma (fscanf and fgets)__________________
The 3 Laws of the Procrastination Society: 1) Never do today that which can be put off until tomorrow 2) Tomorrow never comes |
|
#4
|
||||
|
||||
Re: Scanning dilemma (fscanf and fgets)Hello there,
to begin with I would like to show you the functions fill_in and list_names with tag *p as the formal parameter followed by the syntax errors that it produces on my machine. So heres the other version of the program with the modified functions: CPP / C++ / C Code:
Code:
Secondly with the previous version of the program which you saw in my earlier post above (plus some minor cosmetic changes for better readability), and with the number of iterations changed to 4 I get the following output Code:
So guessing from the run what I can say is that on entering a textual input for a function that expects to receive an integer the program goes crazy and behaves in a weird pattern. Additionally the rate variable remains uninitialized and hence stands at 0,000000 for all the employees. What strikes me is that what I entered for age in the first iteration turns up in place of [i]p.fname in the second iteration which is rather strange. But as waltp has said fscanf and all variants of scanf are rather strange. What do you think is causing such havoc and what would be the best way to avoid such strange behavior?..do u think doing a typecheck on what the user inputs using functions like !(isalpha) the way to go about it? Hope to hear from you, __________________
Hope to hear from you guys! -------------------------------------------------- Best Regards, Aijaz Baig. |
|
#5
|
|||||||||
|
|||||||||
Re: Scanning dilemma (fscanf and fgets)Quote:
OK, here we go: Quote:
In the function declaration, the variable p is a pointer to a struct. Therefore, as I tried to point out, p[i] is a struct, and, given that fname is the name of an array, the way you use fgets() to read something into the fname member could be CPP / C++ / C Code:
Quote:
Quote:
Since age is an int variable, you need its address in the scanf function, so the way to read something into that, using fscanf could be: CPP / C++ / C Code:
(More about that in a minute.) Quote:
CPP / C++ / C Code:
Quote:
What is the declared return type of the function that you wrote. What is the data type of of the expression is in your return statement? Quote:
So, there are some questions that you have to ask yourself when this happens (If you don't know, don't guess; I'll tell you in a minute). 1. What is the data type of whatever it is that you are trying to read after the enter age prompt? 2. What did you enter? 3. What happens when fscanf can't convert the input to the indicated data type? Quote:
Oh, you already guessed. Well, I would say that you are a bad guesser. The pattern is absolutely predictable; it just doesn't do what you apparently want it to do. (And what is it that you want it to do?) I'm not sure what you expect it to do when you give it an invalid input. But here's what it does: With a "%d" format specifier, scanf ignores leading whitespace (including the newline character left in the buffer from the previous input), then it converts as much of the input as it can and stops when it encounters something that is not a decimal digit. Since the first non-whitespace character that it sees is the character 's', it stops right there. The next scanf will start at that same position. (Scanf did not change the contents of the memory location where you told it to store the integer.) The next scanf statement is with a "%f" format, so it tries, but fails to convert something starting with a 's' to a floating point number, so it stays right there for the next scanf statement. Bottom line: When reading numerical quantities, you should always (always) test to see whether scanf was successful. If not, then you can try to recover (in a number of different ways), or you can simply make the program quit. For example: CPP / C++ / C Code:
Regards, Dave Footnote: Quote:
I'm not sure that those are the exact words that Walt used, but even if they are, it's a matter of opinion. I don't find anything strange about their behavior. (But that's only my opinion.) The functions are well-defined in the C standard, and act in predictable ways. Now Walt and I both have a problem with the way you used fscanf("%s"...), since there is no way to protect excessive user input from overflowing your input array, and that leads to undefined behavior (the dreaded "buffer overflow" problem that has lead to so many virus and other exploits). There is a way, but that ain't it. But that's another story. I just wanted to get you past the point where you were guessing at what is happening and wasting your time trying to do things that wouldn't help to get your program running. |
|
#6
|
||||
|
||||
Re: Scanning dilemma (fscanf and fgets)HI.
Thnks for pointing out a lot of things there. In particular what I missed was that when I declare the formal parameter as tag *p instead of tag p[], the way to access an element of the structure is still the same viz. p[i].element_name. This would have been different if I would have had just a single struct to access as in: p -> element_name. What I forgot was that the array notation p[i] automatically translates into *(p + i) so after pointing to the correct address it gets deferenced.. ...what I even missed was that p[i] would be evaluated first followed by the dot operator and hence a lot of confusion ensued..well..it was really silly..but.thats a part of learning I guess...constructive criticism always helps... Furthermore, with regards to the scanf issue I should have read about the behavior of scanf in a bit more detail..I should not take the functionality of such oft-used naive looking functions for granted.. Quote:
..as it safely scans in whatever is currently in the input buffer(until the first whitespace) into the memory location where p[1].fname resides. Hence p[1].fname gets the chars that I entered for p[0].age isn't it? Hence now I know why it waits for 'lname' but not for 'fname. And the pattern repeats itself over all the iterations. So I guess for the last iteration when I enter some non-numeric chars for age, scanf stops at the first letter but the program quits. So it means that my input buffer is still populated with 'slvms' isn't it? Well..now I do see things much more clearly...well..I should be doing much more reading for sure.... __________________
Hope to hear from you guys! -------------------------------------------------- Best Regards, Aijaz Baig. |
|
#7
|
|||
|
|||
Re: Scanning dilemma (fscanf and fgets)Quote:
It's always OK to ask. No one was born knowing this stuff, and they don't always cover it in elementary courses or textbooks (or maybe they don't emphasize it enough for it to soak in). Regards, Dave |
|
#8
|
|||
|
|||
Re: Scanning dilemma (fscanf and fgets)Quote:
My response and example run of the program showed that it could actually work for some user inputs. In a later post I pointed out what you should do if you use scanf() for numeric variables. I also mentioned using scanf("%s"...) is not safe, since users could enter more chars than the array could handle, leading to buffer overrun that causes undefined behavior. In your second post you mixed fgets() and scanf(), and that leads to problems that you must handle. It's do-able, but requires a little study. So, here's a summary of "string" reading functions for char arrays: scanf("%s", x)
Note that after scanf("%s",...) or scanf("%d",...) or scanf("%f",...) there is always at least a newline char left in the input buffer. That bears repeating: There may be more stuff, but there is always a newline left in the system input buffer that will be encountered by the next scanf() (or fgets(), or getchar(), or whatever). Now, it is possible to use scanf to read a "string" that is safe (that is will not overrun your buffer). I'll give an example. Suppose you have the following: CPP / C++ / C Code:
Now, after skipping leading whitespace (if any), scanf will read at most 19 chars from user input. It will stop scanning at that point (so if there were more than 19 chars in the input buffer, they are still there for the next scanf or other input function). A terminating zero-byte is stored in the x array after all of the user input chars that it got. (So the array size must be at least one more than the number you use in the format specifier.) If the user enters fewer than 19 chars, then that many are stored in x, and a terminating zero byte is stored. The newline char is not stores in the array; it is left in the input buffer. This is "safe," since it won't write more chars than your array must hold. Note that it always writes the terminating zero byte, so the result in the x array is a legal C-style "string." Note that if the user enters more than 19 chars, the rest of them are still in the input buffer. If you want to empty out the input buffer so that the next input function starts on a "fresh" user input line, then it's simple. One way: A loop that reads and discards chars until it has found the newline at the end of the user input: CPP / C++ / C Code:
A reminder: If the next input function that your program will use is another scanf() for a "string" or a numeric quantity, and the only thing left in the input buffer is the newline char, it is not necessary to "eat" it, since leading whitespace will be ignored by the next scanf Now, here's the way that many experienced programmers use: fgets(buffer, size, stdin) Properties of fgets()
If this seems to be a lot of hassle for simple programs, maybe it is. And some people may even say that simple programs may not need the "protection" that assures no buffer overruns, but for purposes of establishing reasonable programming practices that will take you forward, I think that getting into the habit of protecting programs from invalid user input programs is a vital part of your development. Today, even after all of those years of experience with exploits (virus and other) of "buffer overruns," and with all of the years of experience with and knowledge of ways to prevent them, we still see (sometimes, several times a week) new security alerts concerning with old, established and respected programs. (Windows and Linux applications, too). Summary
Regards, Dave |
|
#9
|
||||
|
||||
Re: the scanning dilemma (fscanf and fgets)Quote:
Lucky for you Dave doesn't mind typing a lot to explain in detail what you could have learned in a few minutes by following a simple link. __________________
The 3 Laws of the Procrastination Society: 1) Never do today that which can be put off until tomorrow 2) Tomorrow never comes |
|
#10
|
||||
|
||||
Re: Scanning dilemma (fscanf and fgets)Hi there walt.
Well..I should certainly have done that..when I look over it now..it has some pretty fine examples...you guys are doing a great job...better than a lot of texts out there (when it comes to explanation). Thanks again for reminding me on that link. I am planning to take a hard copy of some of the threads that I have started over time plus some very important articles like that one. I cannot seem to find the printable version of that article. Additionally the printable version of threads comes up with the code part all garbled ..like this. It would be great if one would be able to print these wonderful articles and discussions so as to refer to them whenever needed. Hope to hear from you soon, Regards, __________________
Hope to hear from you guys! -------------------------------------------------- Best Regards, Aijaz Baig. |
Recent GIDBlog
Install Adobe Flash - Without Administrator Rights by LocalTech
| Thread Tools | Search this Thread |
| Rate This Thread | |
|
|
Network Sites: GIDNetwork · GIDWebHosts · GIDSearch · Learning Journal by J de Silva, The