Skip to content

vsprintf articles update #5230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 11, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,14 @@ See [Behavior summary](#behavior-summary) for details.

## Remarks

**`vsnprintf_s`** is identical to **`_vsnprintf_s`** and is included for conformance to the ANSI standard. **`_vnsprintf`** is retained for backward compatibility.
Each of these functions takes a pointer to an argument list, then formats and writes up to *`count`* characters of the given data to the memory pointed to by *`buffer`* and appends a terminating `NULL`.

Each of these functions takes a pointer to an argument list, then formats and writes up to *`count`* characters of the given data to the memory pointed to by *`buffer`* and appends a terminating null.
In debug builds, the remaining `sizeOfBuffer` bytes following the terminating `NULL` are filled with 'xFE' as described in [`_CrtSetDebugFillThreshold`](crtsetdebugfillthreshold.md).

The versions of these functions with the **`_l`** suffix are identical except that they use the locale parameter passed in instead of the current thread locale.

**`vsnprintf_s`** is identical to **`_vsnprintf_s`** and is included for conformance to the ANSI standard. **`_vnsprintf`** is retained for backward compatibility.

### Behavior summary

For the following table:
Expand All @@ -127,6 +129,7 @@ For the following table:
| `buffer == NULL` and `sizeOfBuffer == 0` and `count == 0` | No data is written. | 0 | N/A | No |
| `buffer == NULL` and either `sizeOfBuffer != 0` or `count != 0` | If execution continues after invalid parameter handler executes, sets `errno` and returns a negative value.| -1 | `EINVAL` (22) | Yes |
| `buffer != NULL` and `sizeOfBuffer == 0` | No data is written. If execution continues after invalid parameter handler executes, sets `errno` and returns a negative value. | -1 | `EINVAL` (22) | Yes |
| `buffer != NULL` and `sizeOfBuffer != 0` and `count == 0` | The buffer is `NULL` terminated. | -1 | N/A | No |
| `count == 0`| Doesn't write any data and returns the number of characters that would have been written, not including the terminating `NULL`. | The number of characters that would have been written not including the terminating `NULL`. | N/A | No |
| `count < 0` | Unsafe: the value is treated as unsigned, likely creating a large value that results in overwriting the memory that follows the buffer. | The number of characters written, not including the terminating `NULL`. | N/A | No |
| `count < sizeOfBuffer` and `len <= count` | All of the data is written and a terminating `NULL` is appended. | The number of characters written. | N/A | No |
Expand All @@ -144,7 +147,7 @@ For information about these and other error codes, see [`_doserrno`, `errno`, `_
> Starting in Windows 10 version 2004 (build 19041), the `printf` family of functions prints exactly representable floating point numbers according to the IEEE 754 rules for rounding. In previous versions of Windows, exactly representable floating point numbers ending in '5' would always round up. IEEE 754 states that they must round to the closest even digit (also known as "Banker's Rounding"). For example, both `printf("%1.0f", 1.5)` and `printf("%1.0f", 2.5)` should round to 2. Previously, 1.5 would round to 2 and 2.5 would round to 3. This change only affects exactly representable numbers. For example, 2.35 (which, when represented in memory, is closer to 2.35000000000000008) continues to round up to 2.4. Rounding done by these functions now also respects the floating point rounding mode set by [`fesetround`](fegetround-fesetround2.md). Previously, rounding always chose `FE_TONEAREST` behavior. This change only affects programs built using Visual Studio 2019 version 16.2 and later. To use the legacy floating point rounding behavior, link with ['legacy_stdio_float_rounding.obj`](../link-options.md).

> [!NOTE]
> To ensure that there is room for the terminating null, be sure that *`count`* is strictly less than the buffer length, or use `_TRUNCATE`.
> To ensure that there is room for the terminating `NULL`, be sure that *`count`* is strictly less than the buffer length, or use `_TRUNCATE`.

In C++, using these functions is simplified by template overloads; the overloads can infer buffer length automatically (eliminating the need to specify a size argument) and they can automatically replace older, non-secure functions with their newer, secure counterparts. For more information, see [Secure template overloads](../secure-template-overloads.md).

Expand Down Expand Up @@ -209,4 +212,4 @@ nSize: -1, buff: Hi there!
[`fprintf`, `_fprintf_l`, `fwprintf`, `_fwprintf_l`](fprintf-fprintf-l-fwprintf-fwprintf-l.md)\
[`printf`, `_printf_l`, `wprintf`, `_wprintf_l`](printf-printf-l-wprintf-wprintf-l.md)\
[`sprintf`, `_sprintf_l`, `swprintf`, `_swprintf_l`, `__swprintf_l`](sprintf-sprintf-l-swprintf-swprintf-l-swprintf-l.md)\
[`va_arg`, `va_copy`, `va_end`, `va_start`](va-arg-va-copy-va-end-va-start.md)
[`va_arg`, `va_copy`, `va_end`, `va_start`](va-arg-va-copy-va-end-va-start.md)
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ For the following table:
| Encoding error during formatting | If processing string specifier `s`, `S`, or `Z`, format specification processing stops. | -1 | `EILSEQ` (42) | No |
| Encoding error during formatting | If processing character specifier `c` or `C`, the invalid character is skipped. The number of characters written isn't incremented for the skipped character, nor is any data written for it. Processing the format specification continues after skipping the specifier with the encoding error. | The number of characters written, not including the terminating `NULL`. | `EILSEQ` (42) | No |
| `buffer == NULL` and `count != 0` | If execution continues after invalid parameter handler executes, sets `errno` and returns a negative value.| -1 | `EINVAL` (22) | Yes |
| `count == 0` | No data is written | The number of characters that would have been written, not including the terminating `NULL`. You can use this result to allocate sufficient buffer space for the string and a terminating `NULL`, and then call the function again to fill the buffer. | N/A | No |
| `buffer == NULL` and `count == 0` | No data is written | The number of characters that would have been written, not including the terminating `NULL`. You can use this result to allocate sufficient buffer space for the string and a terminating `NULL`, and then call the function again to fill the buffer. | N/A | No |
| `count == 0` | No data is written | -1 | `ERANGE` (34) | No |
| `count < 0`| Unsafe: the value is treated as unsigned, likely creating a large value that results in overwriting the memory that follows the buffer. | The number of characters written. | N/A | No |
| `count < sizeOfBuffer` and `len <= count` | All of the data is written and a terminating `NULL` is appended. | The number of characters written, not including the terminating `NULL`. | N/A | No |
| `count < sizeOfBuffer` and `len > count` | The first *`count-1`* characters are written followed by a null-terminator. | The number of characters that would have been written had `count` matched the number of characters to output, not including the null-terminator. | N/A | No |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ The locale to use.

## Return value

**`_vsprintf_p`** and **`_vswprintf_p`** return the number of characters written, not including the terminating null character, or a negative value if an output error occurs.
**`_vsprintf_p`** and **`_vswprintf_p`** return the number of characters written, not including the terminating `NULL` character, or a negative value if an output error occurs.
If the *`buffer`* is a `NULL` pointer and *`sizeInBytes`* or *`count`* are zero, functions return the number of characters that would have been written not including the terminating `NULL`.
If the *`buffer`* is valid and *`sizeInBytes`* or *`count`* are zero, returns -1.

## Remarks

Expand All @@ -76,7 +78,7 @@ These functions differ from the `vsprintf_s` and `vswprintf_s` only in that they

The versions of these functions with the `_l` suffix are identical except that they use the locale parameter passed in instead of the current thread locale.

If the *`buffer`* or *`format`* parameters are `NULL` pointers, if count is zero, or if the format string contains invalid formatting characters, the invalid parameter handler is invoked, as described in [Parameter validation](../parameter-validation.md). If execution is allowed to continue, the functions return -1 and set `errno` to `EINVAL`.
If the *`buffer`* or *`format`* parameters are `NULL` pointers, or if the format string contains invalid formatting characters, the invalid parameter handler is invoked, as described in [Parameter validation](../parameter-validation.md). If execution is allowed to continue, the functions return -1 and set `errno` to `EINVAL`.

> [!IMPORTANT]
> Starting in Windows 10 version 2004 (build 19041), the `printf` family of functions prints exactly representable floating point numbers according to the IEEE 754 rules for rounding. In previous versions of Windows, exactly representable floating point numbers ending in '5' would always round up. IEEE 754 states that they must round to the closest even digit (also known as "Banker's Rounding"). For example, both `printf("%1.0f", 1.5)` and `printf("%1.0f", 2.5)` should round to 2. Previously, 1.5 would round to 2 and 2.5 would round to 3. This change only affects exactly representable numbers. For example, 2.35 (which, when represented in memory, is closer to 2.35000000000000008) continues to round up to 2.4. Rounding done by these functions now also respects the floating point rounding mode set by [`fesetround`](fegetround-fesetround2.md). Previously, rounding always chose `FE_TONEAREST` behavior. This change only affects programs built using Visual Studio 2019 version 16.2 and later. To use the legacy floating point rounding behavior, link with ['legacy_stdio_float_rounding.obj`](../link-options.md).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ The locale to use.

**`vsprintf`** and **`vswprintf`** return the number of characters written, not including the terminating `NULL` character, or a negative value if an output error occurs. If *`buffer`* or *`format`* is a `NULL` pointer, these functions invoke the invalid parameter handler, as described in [Parameter validation](../parameter-validation.md). If execution is allowed to continue, these functions return -1 and set `errno` to `EINVAL`.

If *`buffer`* is a `NULL` pointer and *`count`* is zero, **`vswprintf`** and **`_vswprintf_l`** return the number of characters that would have been written not including the terminating NULL.

If *`buffer`* is valid and *`count`* is zero, **`vswprintf`** and **`_vswprintf_l`** return -1. The contents of *`buffer`* are unchanged.

For information on these and other error codes, see [`errno`, `_doserrno`, `_sys_errlist`, and `_sys_nerr`](../errno-doserrno-sys-errlist-and-sys-nerr.md).

## Remarks
Expand Down