snprintf return value

Do you have a question? Post it now! No Registration Necessary

Translate This Thread From English to

Threaded View
I usually have a static buffer and I don't want to use a dynamic buffer.

char buf[BUFLEN];

I have to construct a long string, and it is quite easy to call snprintf  
multiple times:

   int n = 0;
   n += snprintf(&buf[n], BUFLEN - n, ...);
   n += snprintf(&buf[n], BUFLEN - n, ...);
   n += snprintf(&buf[n], BUFLEN - n, ...);
   n += snprintf(&buf[n], BUFLEN - n, ...);

I was convinced the return value was the real number of characters  
written in buf, not counting final ''. In this case, the above code works.

I just noticed the return value of snprintf() is the number of  
characters of the generated string as if the buffer was long enough. So  
the above code doesn't work.

The return value of snprintf() is useful if you want to dynamically  
reallocate the buffer so it can store all the string. In my case I would  
prefer to truncate the string at the end of the static buffer. The above  
code should be redesigned as:

   int n = 0;
   n += snprintf(&buf[n], BUFLEN - n, ...);
   if (n >= BUFLEN - n) return;
   n += snprintf(&buf[n], BUFLEN - n, ...);
   if (n >= BUFLEN - n) return;
   n += snprintf(&buf[n], BUFLEN - n, ...);
   if (n >= BUFLEN - n) return;
   n += snprintf(&buf[n], BUFLEN - n, ...);
   if (n >= BUFLEN - n) return;

The code starts being not readable. I'm thinking to create a new  
snprintf2():

   int
   snprintf2(char *str, size_t size, const char *format, ...) {
       va_list args;

       va_start(args, format);
       int n = vsnprintf(str, size, format, args);
       va_end(args);
       return (n >= size) ? (size - 1) : n;
   }

Re: snprintf return value
AT Monday 10 December 2018 18:20, pozz wrote:

Quoted text here. Click to load it

Keep in mind that snprintf can even return negative values as error codes.

--  
Reinhardt


Re: snprintf return value
Il 10/12/2018 11:24, Reinhardt Behm ha scritto:
Quoted text here. Click to load it

Could you provide a test case for a negative return value from snprintf()?


Re: snprintf return value
On 10/12/18 11:20, pozz wrote:
Quoted text here. Click to load it

This is going to add a good extra layer of inefficiency, stop
optimisations that your compiler could do (such as converting some cases
to strcat), and stop the format checking provided by gcc unless you
manually add the right attributes.

I'd be tempted to put things in a macro here:

#define snprintf2(str, size, format...) \
    ({ int n = snprintf(str, size, ## format); \
       (n >= size) ? (size - 1) : n })

Yes, it's a gcc extension - but the point is to get the gcc features of
warnings and optimisation.  If you are not using gcc, then I guess your
function is okay.



Re: snprintf return value
On 10/12/18 9:20 pm, pozz wrote:
Quoted text here. Click to load it

I'd be seriously asking yourself why you prefer that.
Arbitrary unexpected truncation of data should not be tolerated.

Clifford Heath.

Re: snprintf return value
On 12/10/18 4:49 PM, Clifford Heath wrote:
Quoted text here. Click to load it

Well, overrunning a buffer is a bug, so it's just a choice of which type  
of misbehaviour you prefer.  If I'm writing to some logfile, for  
instance, truncating the message is a lot better than overwriting memory!

Cheers

Phil Hobbs

--  
Dr Philip C D Hobbs
Principal Consultant
We've slightly trimmed the long signature. Click to see the full one.
Re: snprintf return value
On 12/12/18 3:41 am, Phil Hobbs wrote:
Quoted text here. Click to load it

Quoted text here. Click to load it

Right, overflow protection is important. So is data integrity.

So make sure you have a big enough buffer for your data, possibly by  
truncating the data before you snprintf(), or by using %.23s or the  
like. Either way you should handle truncation *deliberately*, not by  
simply clamping to a buffer length.

Clifford Heath.

Re: snprintf return value
On 12/12/18 00:34, Clifford Heath wrote:
Quoted text here. Click to load it

Not all data is equally valuable.  And not all data is of a size that
you know you can handle fully.  I have no idea what the OP's original
needs are here, but a prime example is, as Phil said, a log file.  Often
the details of what are in the log file are not critical - but avoiding
buffer overruns or limiting the bandwidth used /is/ critical.  So
techniques like truncation or rate limiting are entirely reasonable here.




Re: snprintf return value
On 12/12/18 7:09 pm, David Brown wrote:
Quoted text here. Click to load it

And as long as that's deliberate, that's fine. But the OP's code was  
appending some number of strings to some buffer. It looked far less  
deliberate than it ought, though the effect may be as intended. It also  
splits the buffer-bounds check into many pieces, so any slip fails the  
whole thing.

We used to have a printf that took a putchar function as argument. You  
could pass one that filled a buffer with truncation. Even easier now  
that C++ supports lambda closures.

Clifford Heath.

Re: snprintf return value
Am 10.12.2018 um 11:20 schrieb pozz:
Quoted text here. Click to load it






What exactly do you expect to gain by doing it that way?

Quoted text here. Click to load it

Convinced by what?  I mean, come on, what do we (as a community) pay
documentation writers for, if nobody can be bothered to read what they
created?

Quoted text here. Click to load it

And that's not at all a coincidence.  It's precisely the reason why the
return value of v?snprintf() is defined slightly differently from that
of all the other *printf() functions.

Quoted text here. Click to load it










Or much simpler as

   snprintf(buf, BUFLEN,
      ...
      ...
      ...
      ...
   );
   buf[BUFLEN - 1] = '';  // just in case we hit bottom

That said, if you absolutely have to, there's always the %n output format...

Re: snprintf return value

Quoted text here. Click to load it

One would assume there's some logic between appending bits of string.

Quoted text here. Click to load it

Now who needs to read the documentation? snprintf guarantees always to  
null-terminate.

Clifford Heath.


Site Timeline