Proper use of scanf

L

Lefty Bigfoot

Hello,

I am aware that a lot of people are wary of using scanf,
because doing it improperly can be dangerous. I have
tried to find a good tutorial on all the ins and outs
of scanf() but been unsuccessful.

Is there a well-respected (by the c.l.c crowd) book
or tutorial that really covers scanf in detail?
 
P

pete

Lefty said:
Hello,

I am aware that a lot of people are wary of using scanf,
because doing it improperly can be dangerous. I have
tried to find a good tutorial on all the ins and outs
of scanf() but been unsuccessful.

Is there a well-respected (by the c.l.c crowd) book
or tutorial that really covers scanf in detail?

I don't know of one.
Here's two examples of one way to use scanf safely.
If you enter more than LENGTH characters,
the extra ones will be automatically discarded.
If you have questions, I can try to explain.
The whole topic of scanf is somewhat lengthy.

/* BEGIN anystring.c */

#include <stdio.h>

#define LENGTH 20
#define str(x) #x
#define xstr(x) str(x)

int main(void)
{
char string[LENGTH + 1];
int rc;

printf("Enter any string or enter an empty string to quit: ");
fflush(stdout);
rc = scanf("%" xstr(LENGTH) "[^\n]%*[^\n]", string);
if (!feof(stdin)) {
getchar();
}
while (rc == 1) {
printf("Your string was %s\n", string);
printf("scanf() returned %d\n", rc);
printf("Enter any string or enter an empty string to quit: ");
fflush(stdout);
rc = scanf("%" xstr(LENGTH) "[^\n]%*[^\n]", string);
if (!feof(stdin)) {
getchar();
}
}
printf("scanf() returned %d\n", rc);
return 0;
}

/* END anystring.c */


/* BEGIN grade.c */

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

#define LENGTH 3
#define str(x) # x
#define xstr(x) str(x)

int main(void)
{
int rc;
char array[LENGTH + 1];
long number;
const char letter[4] = "DCBA";

fputs("Enter the Numeric grade: ", stdout);
fflush(stdout);
rc = scanf("%" xstr(LENGTH) "[^\n]%*[^\n]", array);
if (!feof(stdin)) {
getchar();
}
while (rc == 1) {
number = strtol(array, NULL, 10);
if (number > 60) {
if (number > 99) {
number = 99;
}
array[0] = letter[(number - 60) / 10];
switch (number % 10) {
case 0:
case 1:
array[1] = '-';
array[2] = '\0';
break;
case 8:
case 9:
array[1] = '+';
array[2] = '\0';
break;
default:
array[1] = '\0';
break;
}
} else {
array[0] = 'F';
array[1] = '\0';
}
printf("The Letter grade is: %s\n", array);
fputs("Enter the Numeric grade: ", stdout);
fflush(stdout);
rc = scanf("%" xstr(LENGTH) "[^\n]%*[^\n]", array);
if (!feof(stdin)) {
getchar();
}
}
return 0;
}

/* END grade.c */
 
J

Jean-Claude Arbaut

Le 19/06/2005 00:54, dans (e-mail address removed),
« Lefty Bigfoot » said:
Hello,

I am aware that a lot of people are wary of using scanf,
because doing it improperly can be dangerous. I have
tried to find a good tutorial on all the ins and outs
of scanf() but been unsuccessful.

Is there a well-respected (by the c.l.c crowd) book
or tutorial that really covers scanf in detail?

Yes, the ISO 9899-1999 Standard, section 7.19.6.4.
I can swear it's well respected here :)

There is also the POSIX standard, at
http://www.opengroup.org/onlinepubs/000095399/
certainly well respected too.

If you want a tutorial, maybe the K&R C book will help you.

If you have UNIX or Linux, try "man scanf"
 
C

CBFalconer

Lefty said:
I am aware that a lot of people are wary of using scanf,
because doing it improperly can be dangerous. I have
tried to find a good tutorial on all the ins and outs
of scanf() but been unsuccessful.

Is there a well-respected (by the c.l.c crowd) book
or tutorial that really covers scanf in detail?

Take a look at the links in my sig. Especially the one marked
C-library, and then the C99 one.

--
Some useful references about C:
<http://www.ungerhu.com/jxh/clc.welcome.txt>
<http://www.eskimo.com/~scs/C-faq/top.html>
<http://benpfaff.org/writings/clc/off-topic.html>
<http://anubis.dkuug.dk/jtc1/sc22/wg14/www/docs/n869/> (C99)
<http://www.dinkumware.com/refxc.html> (C-library}
<http://gcc.gnu.org/onlinedocs/> (GNU docs)
 
V

Vivek

There seems to be some problem with the way I am using scanf() in my
code. Could please help me out?

/*Sample Program*/

#include<stdio.h>

int main()
{
double D;

scanf("%g", &D);
printf("%g", D);

return 0;
}

/*During Execution*/
1232 /*Input*/

5.68642e-315/*Output*/

/*End*/

If it helps, I am using the gcc compiler(ver 3.4.3) that I got with
DJGPP and I am using it on Win98.
 
V

Vivek

Hello,

I just found a post from a person having the same problem. How cool is
that! The one mistake I found out is that '%g' in the scanf should be
'%lg'. Anyother nit picking will be welcomed.
 
M

Martin Ambuhl

Vivek said:
There seems to be some problem with the way I am using scanf() in my
code. Could please help me out?

/*Sample Program*/
#include<stdio.h>
int main()
{
double D;
scanf("%g", &D);
printf("%g", D);
return 0;
}

/*During Execution*/
1232 /*Input*/
5.68642e-315/*Output*/


#include<stdio.h>

int main(void)
{
double D;
char input[] = "1234";

printf("Using the OP's incorrect specifier for\n"
"reading the string \"%s\" as a double:", input);
sscanf(input, "%g", &D);
printf("%g\n", D);

printf("Using the correct specifier for\n"
"reading the string \"%s\" as a double:", input);
sscanf(input, "%lg", &D);
printf("%g\n", D);

return 0;
}

[output]
Using the OP's incorrect specifier for
reading the string "1234" as a double:2.88675e-13
Using the correct specifier for
reading the string "1234" as a double:1234
If it helps, I am using the gcc compiler(ver 3.4.3) that I got with
DJGPP and I am using it on Win98.

If you had turned on the warnings during compilation, gcc would have
told you the specifier was wrong. Not all compilers are so obliging.
Keep those warning levels up, and don't ignore the diagnostics.
 
E

Eric Sosman

Vivek said:
There seems to be some problem with the way I am using scanf() in my
code. Could please help me out?
[...]
double D;
scanf("%g", &D);
[...]

If it helps, I am using the gcc compiler(ver 3.4.3) that I got with
DJGPP and I am using it on Win98.

The compiler you're using happens to be able to
catch this error for you, if you ask it nicely. Use
the command-line flags "-Wall -W"; for code that's
intended to be portable use "-Wall -W -ansi -pedantic".
(There are alternatives to "-ansi"; see the info pages.)
 
C

CBFalconer

Eric said:
Vivek said:
There seems to be some problem with the way I am using scanf()
in my code. Could please help me out?
[...]
double D;
scanf("%g", &D);
[...]

If it helps, I am using the gcc compiler(ver 3.4.3) that I got
with DJGPP and I am using it on Win98.

The compiler you're using happens to be able to
catch this error for you, if you ask it nicely. Use
the command-line flags "-Wall -W"; for code that's
intended to be portable use "-Wall -W -ansi -pedantic".
(There are alternatives to "-ansi"; see the info pages.)

This is one more of a long list of anomalies in scanf, and adds to
the list of reasons not to use it. Unfortunately the C library
lacks good routines to input from streams (rather than buffers). I
have written and published some suitable routines in the past.

To the OP - look up the modifier l (small ell) in the scanf
documentation. Read it with great care. Then don't use it, get
complete lines and work on those.

--
Some informative links:
http://www.geocities.com/nnqweb/
http://www.catb.org/~esr/faqs/smart-questions.html
http://www.caliburn.nl/topposting.html
http://www.netmeister.org/news/learn2quote.html
 
J

Jack Klein

Le 19/06/2005 00:54, dans (e-mail address removed),


Yes, the ISO 9899-1999 Standard, section 7.19.6.4.
I can swear it's well respected here :)

That's actually not a reference to how to use the function safely, nor
even a recommendation that it should be used.

See 7.19.7.7, which describes the gets() function, and includes not so
much as a hint of a warning about buffer overflows.
There is also the POSIX standard, at
http://www.opengroup.org/onlinepubs/000095399/
certainly well respected too.

If you want a tutorial, maybe the K&R C book will help you.

If you have UNIX or Linux, try "man scanf"

The general consensus here, which I agree with, is that the *scanf()
functions are best avoided by all except extreme experts on their use.
If non-experts avoid them, the will never become extreme experts,
making the point moot.
 
J

Jean-Claude Arbaut

That's actually not a reference to how to use the function safely, nor
even a recommendation that it should be used.

See 7.19.7.7, which describes the gets() function, and includes not so
much as a hint of a warning about buffer overflows.

Oopss :)

The general consensus here, which I agree with, is that the *scanf()
functions are best avoided by all except extreme experts on their use.
If non-experts avoid them, the will never become extreme experts,
making the point moot.

Anyway it's certainly good practice not to use a function when one has
doubts on its use. Concerning scanf, it's rarely useful, because of its
strange behaviour, at least on some implementations (if I remember well, you
may enter an infinite loop if you enter wrong data when an integer is
expected, not merely because of scanf, because of the program that uses it).
I mainly use it in toy programs.
 
E

Eric Sosman

Jack said:
[...]
The general consensus here, which I agree with, is that the *scanf()
functions are best avoided by all except extreme experts on their use.
If non-experts avoid them, the will never become extreme experts,
making the point moot.

I'd agree that scanf() and fscanf() are best avoided,
but sscanf() is both usable and useful. fgets()/sscanf()
make a good combination.
 
R

robert.thorpe

Lefty said:
Hello,

I am aware that a lot of people are wary of using scanf,
because doing it improperly can be dangerous. I have
tried to find a good tutorial on all the ins and outs
of scanf() but been unsuccessful.

Is there a well-respected (by the c.l.c crowd) book
or tutorial that really covers scanf in detail?

There is also a useful reference in the documentation of the GNU
standard C library

http://www.gnu.org/software/libc/manual/html_mono/libc.html.gz#Formatted Output Functions

This also mentions many GNU extensions though.
 
B

Ben Pfaff

Eric Sosman said:
I'd agree that scanf() and fscanf() are best avoided,
but sscanf() is both usable and useful.

Only if one doesn't mind the nasal demons that are emitted when a
conversion's value cannot be represented in the object it is
stored into, e.g. when an out-of-range number is read.
 
E

Eric Sosman

Ben said:
Only if one doesn't mind the nasal demons that are emitted when a
conversion's value cannot be represented in the object it is
stored into, e.g. when an out-of-range number is read.

Hmmm ... I'd always assumed that'd be a "matching
failure," but no: trying to derive an eight-bit `char'
value from "666" is, indeed, U.B. You learn something
new every day.
 
J

Jack Klein

Jack said:
[...]
The general consensus here, which I agree with, is that the *scanf()
functions are best avoided by all except extreme experts on their use.
If non-experts avoid them, the will never become extreme experts,
making the point moot.

I'd agree that scanf() and fscanf() are best avoided,
but sscanf() is both usable and useful. fgets()/sscanf()
make a good combination.

As Ben already pointed out, all the members of *scanf() produce
undefined behavior if the result of a numeric conversion is out of
range for the destination type.

Which means to use them safely, you need to pre-check the numeric
digits before converting. If you are going to check them by hand, you
might as well do the conversion yourself, it takes little additional
effort.

Now fgets() and { strol(), strtod(), strtoul(), strtoll(), stroull() }
make a robust combination.
 
C

CBFalconer

Jack said:
Jack Klein wrote:
[...]
The general consensus here, which I agree with, is that the
*scanf() functions are best avoided by all except extreme
experts on their use. If non-experts avoid them, the will
never become extreme experts, making the point moot.

I'd agree that scanf() and fscanf() are best avoided,
but sscanf() is both usable and useful. fgets()/sscanf()
make a good combination.

As Ben already pointed out, all the members of *scanf() produce
undefined behavior if the result of a numeric conversion is out
of range for the destination type.

Which means to use them safely, you need to pre-check the numeric
digits before converting. If you are going to check them by hand,
you might as well do the conversion yourself, it takes little
additional effort.

Now fgets() and { strol(), strtod(), strtoul(), strtoll(),
stroull() } make a robust combination.

The only problem with that is that you have to assign a buffer. I
have published some routines which operate directly on the input
stream, and need no buffers (as does scanf). However my routines
are not variadic, and do not involve loading a large interpreter
system. They also have comprehensive error reporting, and do such
things as absorbing a complete numeric field even when the value
overflows. These are worthwhile considerations, especially in a
resource starved embedded system.

The following is a sample of the routines:

/*--------------------------------------------------------------
* Read an unsigned value. Signal error for overflow or no
* valid number found. Returns true for error, false for noerror
*
* Skip all leading whitespace on f. At completion getc(f) will
* return the character terminating the number, which may be \n
* or EOF among others. Barring EOF it will NOT be a digit. The
* combination of error, zero, and the following getc returning
* \n indicates that no numerical value was found on the line.
*
* If the user wants to skip all leading white space including
* \n, \f, \v, \r, he should first call "skipwhite(f);"
*
* Peculiarity: This specifically forbids a leading '+' or '-'.
* Peculiarity: This forbids overflow, unlike C unsigned usage.
* on overflow, UINT_MAX is returned.
*/
int readxwd(unsigned int *wd, FILE *f)
{
unsigned int value, digit;
int status;
int ch;

#define UWARNLVL (UINT_MAX / 10U)
#define UWARNDIG (UINT_MAX - UWARNLVL * 10U)

value = 0; /* default */
status = 1; /* default error */

do {
ch = getc(f);
} while ((' ' == ch) || ('\t' == ch)); /* skipblanks */
/* while (isblank((UCHAR)ch)); */ /* for C99 */

if (!(EOF == ch)) {
if (isdigit((UCHAR)ch)) /* digit, no error */
status = 0;
while (isdigit((UCHAR)ch)) {
digit = (unsigned) (ch - '0');
if ((value < UWARNLVL) ||
((UWARNLVL == value) && (UWARNDIG >= digit)))
value = 10 * value + digit;
else { /* overflow */
status = 1;
value = UINT_MAX;
}
ch = getc(f);
} /* while (ch is a digit) */
}
*wd = value;
ungetc(ch, f);
return status;
} /* readxwd */

--
Some informative links:
http://www.geocities.com/nnqweb/
http://www.catb.org/~esr/faqs/smart-questions.html
http://www.caliburn.nl/topposting.html
http://www.netmeister.org/news/learn2quote.html
 
P

Peter Nilsson

CBFalconer said:
...
int ch;
ch = getc(f);
...
if (!(EOF == ch)) {
if (isdigit((UCHAR)ch)) /* digit, no error */

Since getc already returns a value in the range of unsigned
char, there's nothing to gain by adding the UCHAR (presumably
unsigned char) cast.

If you're worried that UCHAR_MAX > INT_MAX, then you should
be performing a specific feof check.
status = 0;
while (isdigit((UCHAR)ch)) {
digit = (unsigned) (ch - '0');
if ((value < UWARNLVL) ||
((UWARNLVL == value) && (UWARNDIG >= digit)))
value = 10 * value + digit;
else { /* overflow */
status = 1;
value = UINT_MAX;
}
ch = getc(f);
} /* while (ch is a digit) */

For unsigned int, you can simplify the overflow check to...

while (isdigit(ch))
{
new_value = 10 * value + ch - '0';
if (new_value < value) { status = 1; new_value = -1; }
value = new_value;
ch = getc(f);
}
 

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,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top