How to calculate the length of the output generated by sprintf?

Purpose : serialize data in JSON.

Problem : I do not know in advance how many characters a long integer.

I thought a good way to do this is to use sprintf()

 size_t length = sprintf(no_buff, "{data:%d}",12312); char *buff = malloc(length); snprintf(buff, length, "{data:%d}",12312); //buff is passed on ... 

Of course, I can use a stack variable, for example char a[256] instead of no_buff .

Question: But is C a utility for one-time records like unix /dev/null ? Smth like this:

 #define FORGET_ABOUT_THIS ... size_t length = sprintf(FORGET_ABOUT_THIS, "{data:%d}",12312); 

ps I know that I can also get the length of an integer through a log, but this way seems more enjoyable.

+7
c printf
source share
5 answers

Since C is a simple language, there is no such thing as “one-time buffers” - all memory management is on the shoulders of programmers (there are GNU C compiler extensions for them, but they are not standard).

I do not know in advance how many characters a long integer.

The solution to the problem is much simpler. snprintf knows!

On C99 compatible platforms, call snprintf with NULL as the first argument:

 ssize_t bufsz = snprintf(NULL, 0, "{data:%d}",12312); char* buf = malloc(bufsz + 1); snprintf(buf, bufsz + 1, "{data:%d}",12312); ... free(buf); 

In older versions of Visual Studio (which have C99 CRT compatible) use _scprintf instead of calling snprintf(NULL, ...) .

+8
source share

You can call int len = snprintf(NULL, 0, "{data:%d}", 12312) to check how much space you need.

snprintf will print no more than size characters, where size is the second argument, and return how many characters would be needed to print everything except for the final '\0' . Since you go to 0, you won’t actually write anything (and thus, an exception from the null pointer that occurs when you try to dereference NULL will be eliminated), but it will still return the length needed to match the whole result you can use to allocate your buffer.

At this point, you can select and print your buffer, without forgetting to include another one for trailing '\0' :

 char *buf = malloc(len + 1); snprintf(buf, len + 1, "{data:%d}", 12312); 
+10
source share

To just get the length you can write:

 int length = snprintf(NULL, 0, "{data:%d}", 12312); 

Note that the return type is int . It can return -1 in case of any error. Make sure your input does not contain long strings, which may result in exceeding the total length of INT_MAX !

+5
source share

If you check the performance, you will run snprintf without an output buffer, at about the same time as the full call.

Therefore, I recommend that you use a smaller buffer just in case and call it only a second time if the return size exceeds the size of the buffer.

In this case, C ++ std::string , but I think you can adapt it for your needs.

 std::string format(const char* format, ...) { va_list args; va_start(args, format); char smallBuffer[1024]; int size = vsnprintf(smallBuffer, sizeof smallBuffer, format, args); va_end(args); if (size < sizeof smallBuffer) return std::string(smallBuffer); char buffer[size + 1]; /* maybe malloc if it too big */ va_start(args, format); vsnprintf(buffer, sizeof buffer, format, args); va_end(args); return std::string(buffer); } 

This code will run 2x faster for lines less than 1k compared to longer ones.

0
source share

This is not a strict answer to your question, but you may find it useful nonetheless. It is not portable, but if you use it on glibc, you can just use asprintf() instead, which will do the memory allocation for you.

-one
source share

All Articles