sprintf

K

krister

Hello,

I'm working in a quite large system that has some limitations. One of
those is that I can't use printf() to get an output on a screen. I'm
forced to use a special function, let's call it PrintOnConsole(), to get
the output on a console. The problem with PrintOnConsole() is that it
only takes strings as input arguments. On the other hand, I'm free to
use sprintf(), so I can convert everything I want to print into a string
and then forward that string to PrintOnConsole().

My problem now is that I don't know how many characters sprintf() will
put in the buffer provided as first input argument.
Here is a code example:

/*-------------------- Begin --------------------------*/

#include <stdio.h>

/* This is just a dummy function, which I don't have access to in reality */
float GimmeAFloat(void)
{
return 34.2;
}

/* This is just a dummy function, which I don't have access to in reality */
void PrintOnConsole(char *string)
{
/* printf() is used here only for testing purposes. It’s not
available in reality in my system */
printf("%s", string);
}


int main(void)
{

float value;
char buf[25];

value = GimmeAFloat();

/* Pre check wanted here. If the value is too long I want to copy the
string “The value is too long\n” to buf instead. */

sprintf(buf, "This is my value: %f\n", value);

PrintOnConsole(buf);

return 0;
}

/*--------------------- End -------------------------*/


Is there any elegant way to do some kind of pre-check how many
characters sprintf() would need?
I’ve used float in this example, but the question is more general. I’d
like to be able to print int, double, long, etc.
I know that sprintf() returns the number of characters printed, but
that’s too late (I’ve already called sprintf() by then).
(It would be nice if it was possible to call sprintf() with NULL as
first argument just to investigate the return value.)

A bonus question: In the code above everything looks OK for me, the
string provided to sprintf() is 18 characters long plus 4 characters for
the float value plus 1 linefeed character plus the terminating null
character makes all in all 24 characters. The output, however, shows:

This is my value: 34.200001


Why isn’t
This is my value: 34.2
printed?


Thanks in advance,
Krister
 
B

Ben Pfaff

I'm working in a quite large system that has some limitations. One of
those is that I can't use printf() to get an output on a screen. I'm
forced to use a special function, let's call it PrintOnConsole(), to
get the output on a console. The problem with PrintOnConsole() is that
it only takes strings as input arguments. On the other hand, I'm free
to use sprintf(), so I can convert everything I want to print into a
string and then forward that string to PrintOnConsole().

My problem now is that I don't know how many characters sprintf() will
put in the buffer provided as first input argument.

Does your implementation have snprintf? snprintf allows you to
specify the maximum number of characters to write to the output
string. If your buffer is too small, you can then allocate a
larger one and try again.
 
W

Walter Roberson

Is there any elegant way to do some kind of pre-check how many
characters sprintf() would need?

See if you have snprintf() [Notice the extra 'n']. If you do and you
provide it with a buffer of size 0, it will return the number of
characters that would be needed to output the arguments, but without
actually outputing anything.
 
C

CryptiqueGuy

Hello,

I'm working in a quite large system that has some limitations. One of
those is that I can't use printf() to get an output on a screen. I'm
forced to use a special function, let's call it PrintOnConsole(), to get
the output on a console. The problem with PrintOnConsole() is that it
only takes strings as input arguments. On the other hand, I'm free to
use sprintf(), so I can convert everything I want to print into a string
and then forward that string to PrintOnConsole().

My problem now is that I don't know how many characters sprintf() will
put in the buffer provided as first input argument.
Here is a code example:

/*-------------------- Begin --------------------------*/

#include <stdio.h>

/* This is just a dummy function, which I don't have access to in reality */
float GimmeAFloat(void)
{
return 34.2;

}

/* This is just a dummy function, which I don't have access to in reality */
void PrintOnConsole(char *string)
{
/* printf() is used here only for testing purposes. It's not
available in reality in my system */
printf("%s", string);

}

int main(void)
{

float value;
char buf[25];

value = GimmeAFloat();

/* Pre check wanted here. If the value is too long I want to copy the
string "The value is too long\n" to buf instead. */

sprintf(buf, "This is my value: %f\n", value);

PrintOnConsole(buf);

return 0;

}

/*--------------------- End -------------------------*/

Is there any elegant way to do some kind of pre-check how many
characters sprintf() would need?

No, portably there is no way to count the number of characters that
could be output on a target.
I've used float in this example, but the question is more general. I'd
like to be able to print int, double, long, etc.
I know that sprintf() returns the number of characters printed, but
that's too late (I've already called sprintf() by then).
(It would be nice if it was possible to call sprintf() with NULL as
first argument just to investigate the return value.)

sprintf is as dangerous as gets().
Better use snprintf().
A bonus question: In the code above everything looks OK for me, the
string provided to sprintf() is 18 characters long plus 4 characters for
the float value plus 1 linefeed character plus the terminating null
character makes all in all 24 characters. The output, however, shows:

This is my value: 34.200001

Why isn't
This is my value: 34.2
printed?
Thats the problem with floating point numbers When you use
return 34.2;
there is a conversion from double to float.
Change the return statement to
return 34.2f;
 
M

Morris Dovey

(e-mail address removed) wrote:

| Is there any elegant way to do some kind of pre-check how many
| characters sprintf() would need?

Of course there is. You already know how many places you intend to
print to the right of the decimal point. You can use base ten
logarithms to determine the number of places needed to the left of the
decimal point.

You'll need minimal logic to deal with negative values and sign,
padding spaces, etc., but that's not difficult.

HTH :)
 
B

Barry Schwarz

Hello,

I'm working in a quite large system that has some limitations. One of
those is that I can't use printf() to get an output on a screen. I'm
forced to use a special function, let's call it PrintOnConsole(), to get
the output on a console. The problem with PrintOnConsole() is that it
only takes strings as input arguments. On the other hand, I'm free to
use sprintf(), so I can convert everything I want to print into a string
and then forward that string to PrintOnConsole().

My problem now is that I don't know how many characters sprintf() will
put in the buffer provided as first input argument.
Here is a code example:

/*-------------------- Begin --------------------------*/

#include <stdio.h>

/* This is just a dummy function, which I don't have access to in reality */
float GimmeAFloat(void)
{
return 34.2;
}

/* This is just a dummy function, which I don't have access to in reality */
void PrintOnConsole(char *string)
{
/* printf() is used here only for testing purposes. It’s not
available in reality in my system */
printf("%s", string);
}


int main(void)
{

float value;
char buf[25];

value = GimmeAFloat();

/* Pre check wanted here. If the value is too long I want to copy the
string “The value is too long\n” to buf instead. */

sprintf(buf, "This is my value: %f\n", value);

PrintOnConsole(buf);

return 0;
}

/*--------------------- End -------------------------*/


Is there any elegant way to do some kind of pre-check how many
characters sprintf() would need?
I’ve used float in this example, but the question is more general. I’d
like to be able to print int, double, long, etc.
I know that sprintf() returns the number of characters printed, but
that’s too late (I’ve already called sprintf() by then).
(It would be nice if it was possible to call sprintf() with NULL as
first argument just to investigate the return value.)

A bonus question: In the code above everything looks OK for me, the
string provided to sprintf() is 18 characters long plus 4 characters for
the float value plus 1 linefeed character plus the terminating null
character makes all in all 24 characters. The output, however, shows:

This is my value: 34.200001


Why isn’t
This is my value: 34.2
printed?

How do you think 34.2 is represented in binary? 34.5 is easy as is
34.25 or 34.125 but think about 34.2. How is sprintf to know that
there is only one significant digit after the decimal point? For that
matter, do you know after each call to GimmeAFloat?

But you can write code that handles most cases:

int sign = value<0; will tell you if you need space for a '-'.

int dig = 1+log10(fabs(value)); will tell you how many digits
before the decimal point, if any. Based on this value, you can decide
to use either %f or %e for floating point values (which are always
passed to sprintf as doubles). You can also use this to compute how
many digits you want after the decimal point (call this value dec).

You can then use these values in your pre-check.

If you decide to print, sprintf will let you specify both
minimum field width (apparently not your concern here) and precision
as *. This will cause the function to extract the actual values to
use in the format conversion from arguments which you can compute

For your example, sign would be 0, dig would be 2, and you could
compute dec as 1. You pre-check would be
if (sign+dig+1+dec < your_max)
and you would call sprintf with something like
sprintf(buf, "This is my value: %.*f\n", dec, value);



Remove del for email
 
B

Ben Pfaff

Morris Dovey said:
(e-mail address removed) wrote:

| Is there any elegant way to do some kind of pre-check how many
| characters sprintf() would need?

Of course there is. You already know how many places you intend to
print to the right of the decimal point. You can use base ten
logarithms to determine the number of places needed to the left of the
decimal point.

If that's the desired approach then you could just allocate space
for the maximum possibly needed digits using DBL_MAX_10_EXP as a
basis, I'd guess.
 
K

Keith Thompson

CryptiqueGuy said:
On Jun 14, 9:56 pm, "(e-mail address removed)" <[email protected]>
wrote: [...]
sprintf is as dangerous as gets().

No, it really isn't. gets() is inherently unsafe *unless* you have
control over what input will appear on stdin (which you usually
don't). sprintf() can be used safely if you're sufficiently careful
with the format string, the arguments, and the target string, all of
which are under the control of your program and can be checked before
attempting the call. (But checking in general is about as difficult as
implementing sprintf itself.)
Better use snprintf().

Agreed, if it's available.

[...]
Thats the problem with floating point numbers
Yes.

When you use
return 34.2;
there is a conversion from double to float.

But that's not the reason.
Change the return statement to
return 34.2f;

That's unlikely to help. The value 34.2 is not exactly representable
in either float or double (or long double). Using a larger
floating-point type (it's common to use double rather than float) will
make the error smaller, but not eliminate it.
 
M

Malcolm McLean

Hello,

I'm working in a quite large system that has some limitations. One of
those is that I can't use printf() to get an output on a screen. I'm
forced to use a special function, let's call it PrintOnConsole(), to get
the output on a console. The problem with PrintOnConsole() is that it only
takes strings as input arguments. On the other hand, I'm free to use
sprintf(), so I can convert everything I want to print into a string and
then forward that string to PrintOnConsole().

My problem now is that I don't know how many characters sprintf() will put
in the buffer provided as first input argument.
Here is a code example:

/*-------------------- Begin --------------------------*/

#include <stdio.h>

/* This is just a dummy function, which I don't have access to in reality
*/
float GimmeAFloat(void)
{
return 34.2;
}

/* This is just a dummy function, which I don't have access to in reality
*/
void PrintOnConsole(char *string)
{
/* printf() is used here only for testing purposes. It’s not available
in reality in my system */
printf("%s", string);
}


int main(void)
{

float value;
char buf[25];

value = GimmeAFloat();

/* Pre check wanted here. If the value is too long I want to copy the
string “The value is too long\n” to buf instead. */

sprintf(buf, "This is my value: %f\n", value);

PrintOnConsole(buf);

return 0;
}

/*--------------------- End -------------------------*/


Is there any elegant way to do some kind of pre-check how many characters
sprintf() would need?
I’ve used float in this example, but the question is more general. I’d
like to be able to print int, double, long, etc.
I know that sprintf() returns the number of characters printed, but that’s
too late (I’ve already called sprintf() by then).
(It would be nice if it was possible to call sprintf() with NULL as first
argument just to investigate the return value.)

A bonus question: In the code above everything looks OK for me, the string
provided to sprintf() is 18 characters long plus 4 characters for the
float value plus 1 linefeed character plus the terminating null character
makes all in all 24 characters. The output, however, shows:

This is my value: 34.200001


Why isn’t
This is my value: 34.2
printed?


Thanks in advance,
Krister

You want vsprintf.

void myprintf(char *fmt, ...)
{
/* go through fmt looking for % signs. If you have a string, pull it out
and calculate the length. If an number, make sure the field width isn't
excessive, and add a few bytes. Then add a few more bytes for luck, malloc
it and call vsprintf, the call the console routine with the strign you
created */
}
 
C

CryptiqueGuy

CryptiqueGuy said:
On Jun 14, 9:56 pm, "(e-mail address removed)" <[email protected]>
wrote: [...]
sprintf is as dangerous as gets().

No, it really isn't. gets() is inherently unsafe *unless* you have
control over what input will appear on stdin (which you usually
don't). sprintf() can be used safely if you're sufficiently careful
with the format string, the arguments, and the target string, all of
which are under the control of your program and can be checked before
attempting the call. (But checking in general is about as difficult as
implementing sprintf itself.)
I would rather involve myself in writing "smart codes" using snprint
than involve in doing all sorts of "avoidable" (avoidable using
snprintf) analyses to ensure that my sprintf does not produce UB.
But if OP has to use some archaic C90 compiler, then of course, he has
to do these analyses to ensure that his sprintf does not produce UB.

I agree that sprintf is not as bad as gets.
But both are bad!
They differ only in their degree of badness.

It is something like stating,
"The ruffian sprintf is not as cruel as the pirate leader gets" .;)

The ruffian treats you well as long as you treat it well!
The pirate leader is completely out of your control!
Everything is a matter of sheer luck!
Better use snprintf().

Agreed, if it's available.

[...]
Thats the problem with floating point numbers
Yes.

When you use
return 34.2;
there is a conversion from double to float.

But that's not the reason.
In most of the cases and in this particular case, that is not the
reason. I agree.
That's unlikely to help.
In this case of printing the value, I agree again.
But this is a good practice. What if OP wants to do some floating
point calculation, using 34.2f is "more likely" (but definitely not
guaranteed) to produce the result he intended, rather than using plain
34.2, in which case there is a demotion from double to float,
resulting in loss of precision.
 
K

krister

Walter said:
Is there any elegant way to do some kind of pre-check how many
characters sprintf() would need?

See if you have snprintf() [Notice the extra 'n']. If you do and you
provide it with a buffer of size 0, it will return the number of
characters that would be needed to output the arguments, but without
actually outputing anything.

Thank you (and all others who have replied) very much. This was just the
nice and elegant solution I was dreaming of; I didn’t know snprintf()
existed.
However, I did a quick test using 0 as the second argument to snprintf()
and got -1 returned from snprintf().
So I looked in the man page which says:

The snprintf() function returns the number of characters
formatted, that is, the number of characters that would have
been written to the buffer if it were large enough. If the
value of n is 0 on a call to snprintf(), an unspecified
value less than 1 is returned.

I guess you meant that I should use 1 instead of 0. That also means that
I need to provide a buffer that can hold (at least) one character,
namely the null character.
 
A

Al Balmer

Walter said:
Is there any elegant way to do some kind of pre-check how many
characters sprintf() would need?

See if you have snprintf() [Notice the extra 'n']. If you do and you
provide it with a buffer of size 0, it will return the number of
characters that would be needed to output the arguments, but without
actually outputing anything.

Thank you (and all others who have replied) very much. This was just the
nice and elegant solution I was dreaming of; I didn’t know snprintf()
existed.
However, I did a quick test using 0 as the second argument to snprintf()
and got -1 returned from snprintf().
So I looked in the man page which says:

The snprintf() function returns the number of characters
formatted, that is, the number of characters that would have
been written to the buffer if it were large enough. If the
value of n is 0 on a call to snprintf(), an unspecified
value less than 1 is returned.

It seems that your implementation isn't standards-conforming. Is it
possible that there's a compiler switch that affects this behavior?
The standard says (among other things):
"If n is zero, nothing is written, and s may be a null pointer."

A negative value should mean there was an encoding error.
I guess you meant that I should use 1 instead of 0. That also means that
I need to provide a buffer that can hold (at least) one character,
namely the null character.

Sounds like that may be a workaround.
 
A

Al Balmer

I agree that sprintf is not as bad as gets.
But both are bad!
They differ only in their degree of badness.

It is something like stating,
"The ruffian sprintf is not as cruel as the pirate leader gets" .;)

The ruffian treats you well as long as you treat it well!

That sounds like a good description of the C language.
 
C

Chris Torek

However, I did a quick test using 0 as the second argument to snprintf()
and got -1 returned from snprintf().

When I put snprintf() into 4.4BSD, I forgot to account for a
zero "buffer size" argument. This is fixed in the C99 standard
(and I fixed it in BSD/OS years ago), but others who picked up
my snprintf() might not have fixed it yet.
So I looked in the man page which says:

The snprintf() function returns the number of characters
formatted, that is, the number of characters that would have
been written to the buffer if it were large enough. If the
value of n is 0 on a call to snprintf(), an unspecified
value less than 1 is returned.

I guess you meant that I should use 1 instead of 0. That also means that
I need to provide a buffer that can hold (at least) one character,
namely the null character.

This is a good-enough work-around, and is easy to code -- instead
of:

n = snprintf(NULL, 0, fmt, arg1, arg2, ..., argN);

you just need:

char dummy;
...
n = snprintf(&dummy, 1, fmt, arg1, arg2, ... argN);

(which is actually how I originally fixed my own implementation
internally -- it was easier to create a "dummy" variable than to
allow buf==NULL with size==0).
 

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

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top