Vfprintf vsprintf


Содержание

Vfprintf vsprintf

Записывает форматированный вывод данных используя указатель на список параметров.

Синтаксис

Параметры

Место хранения данных для вывода.

Максимальное число сохраняемых символов.

Список указателя на параметры.

Возвращаемое значение

Функции vsprintf and vswprintf возвращают число записанных символов, включая символ завершающего нуля, или отрицательное значение, если происходит ошибка при выводе.

Замечания

Каждая из этих функций получает указатель на список параметров, а затем форматирует и записывает полученные данные в память указанную при помощи buffer .

Обеспечение безопасности! Обратите внимание! на то, что нет никакого способа ограничить число записанных символов, что означает, что код, используя эти функции восприимчив к переполнению буфера. Используйте _vsnprintf вместо него, или вызовите _vscprintf, чтобы определить, какой величины нужен буфер. Кроме того, убедитесь в том, что формат — не определяемая пользователем строка. Для получения дополнительной информации, см. статью Предотвращение переполнения буфера.

Унифицированно-текстовые стандартные отображения

Vfprintf vsprintf

Самая актуальная документация по Visual Studio 2020: Документация по Visual Studio 2020.

Записывают форматированные выходные данные с помощью указателя на список аргументов. Здесь представлены версии функций vsprintf _vsprintf_l, vswprintf, _vswprintf_l, __vswprintf_l с усовершенствованной безопасностью, как описано в разделе функции безопасности в CRT.

Параметры

buffer
Место хранения выходных данных.

numberOfElements
Размер buffer в символах.

format
Спецификация формата.

argptr
Указатель на список аргументов.

locale
Используемый языковой стандарт.

vsprintf_s и vswprintf_s возвращают число записанных символов, не включая завершающий символ null или отрицательное значение, если произошла ошибка вывода. Если buffer или format является указателем null, если счетчик равен нулю, или если строка формата содержит недопустимые символы форматирования, вызывается обработчик недопустимого параметра, как описано в разделе проверки параметров. Если продолжение выполнения разрешено, функции возвращают значение -1 и задают для errno значение EINVAL .

Сведения об этих и других кодах ошибок см. в разделе _doserrno, errno, _sys_errlist и _sys_nerr.

Каждая из этих функций принимает указатель на список аргументов и затем форматирует и записывает заданных данных в памяти, на который указывает buffer .

vswprintf_s соответствует стандарту ISO C для vswprintf , что требует второго параметра count , типа size_t .

Эти функции отличаются от небезопасных версий только в том, что безопасные версии поддерживает позиционные параметры. Дополнительные сведения см. в разделе позиционные параметры printf_p.

Версии этих функций с суффиксом _l идентичны за исключением того, что они используют переданный параметр языкового стандарта вместо языкового стандарта текущего потока.

В C++ использование данных функций упрощено наличием шаблонных перегрузок; перегруженные методы могут автоматически определять длину буфера (что исключает необходимость указания аргумента с размером буфера), а также они могут автоматически заменять более старые, незащищенные функции их новыми безопасными аналогами. Дополнительные сведения см. в разделе Secure Template Overloads.

Универсальное текстовое сопоставление функций

Подпрограмма TCHAR.H _UNICODE и _MBCS не определены _MBCS определено _UNICODE определено
_vstprintf_s vsprintf_s vsprintf_s vswprintf_s
_vstprintf_s_l _vsprintf_s_l _vsprintf_s_l _vswprintf_s_l
Подпрограмма Обязательный заголовок Необязательные заголовки
vsprintf_s , _vsprintf_s_l и *
vswprintf_s , _vswprintf_s_l или и *

*Необходим для совместимости UNIX V.

Дополнительные сведения о совместимости см. в разделе Совместимость во введении.

vsprintf(3) — Linux man page

printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf — formatted output conversion

Synopsis

int printf(const char *format, . );
int fprintf(FILE *
stream, const char *format, . );
int sprintf(char *
str, const char *format, . );
int snprintf(char *
str, size_t size, const char *format, . );

int vprintf(const char *format, va_list ap);
int vfprintf(FILE *
stream, const char *format, va_list ap);
int vsprintf(char *
str, const char *format, va_list ap);
int vsnprintf(char *
str, size_t size, const char *format, va_list ap);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)): snprintf(), vsnprintf(): _BSD_SOURCE || _XOPEN_SOURCE >= 500 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L;
or cc -std=c99

Description

The functions in the printf() family produce output according to a format as described below. The functions printf() and vprintf() write output to stdout, the standard output stream; fprintf() and vfprintf() write output to the given output stream; sprintf(), snprintf(), vsprintf() and vsnprintf() write to the character string str.

The functions snprintf() and vsnprintf() write at most size bytes (including the terminating null byte (‘\0’)) to str.

The functions vprintf(), vfprintf(), vsprintf(), vsnprintf() are equivalent to the functions printf(), fprintf(), sprintf(), snprintf(), respectively, except that they are called with a va_list instead of a variable number of arguments. These functions do not call the va_end macro. Because they invoke the va_arg macro, the value of ap is undefined after the call. See stdarg(3).

These eight functions write the output under the control of a format string that specifies how subsequent arguments (or arguments accessed via the variable-length argument facilities of stdarg(3)) are converted for output.

C99 and POSIX.1-2001 specify that the results are undefined if a call to sprintf(), snprintf(), vsprintf(), or vsnprintf() would cause copying to take place between objects that overlap (e.g., if the target string array and one of the supplied input arguments refer to the same buffer). See NOTES.

Return value Upon successful return, these functions return the number of characters printed (excluding the null byte used to end output to strings).

The functions snprintf() and vsnprintf() do not write more than size bytes (including the terminating null byte (‘\0’)). If the output was truncated due to this limit then the return value is the number of characters (excluding the terminating null byte) which would have been written to the final string if enough space had been available. Thus, a return value of size or more means that the output was truncated. (See also below under NOTES.)

If an output error is encountered, a negative value is returned.

Format of the format string The format string is a character string, beginning and ending in its initial shift state, if any. The format string is composed of zero or more directives: ordinary characters (not %), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero or more subsequent arguments. Each conversion specification is introduced by the character %, and ends with a conversion specifier. In between there may be (in this order) zero or more flags, an optional minimum field width, an optional precision and an optional length modifier.

The arguments must correspond properly (after type promotion) with the conversion specifier. By default, the arguments are used in the order given, where each ‘*’ and each conversion specifier asks for the next argument (and it is an error if insufficiently many arguments are given). One can also specify explicitly which argument is taken, at each place where an argument is required, by writing «%m$» instead of ‘%’ and «*m$» instead of ‘*’, where the decimal integer m denotes the position in the argument list of the desired argument, indexed starting from 1. Thus, and are equivalent. The second style allows repeated references to the same argument. The C99 standard does not include the style using ‘$’, which comes from the Single UNIX Specification. If the style using ‘$’ is used, it must be used throughout for all conversions taking an argument and all width and precision arguments, but it may be mixed with «%%» formats which do not consume an argument. There may be no gaps in the numbers of arguments specified using ‘$’; for example, if arguments 1 and 3 are specified, argument 2 must also be specified somewhere in the format string.

For some numeric conversions a radix character («decimal point») or thousands’ grouping character is used. The actual character used depends on the LC_NUMERIC part of the locale. The POSIX locale uses ‘.’ as radix character, and does not have a grouping character. Thus, results in «1234567.89» in the POSIX locale, in «1234567,89» in the nl_NL locale, and in «1.234.567,89» in the da_DK locale.

The flag characters The character % is followed by zero or more of the following flags: #

The value should be converted to an «alternate form». For o conversions, the first character of the output string is made zero (by prefixing a 0 if it was not zero already). For x and X conversions, a nonzero result has the string «0x» (or «0X» for X conversions) prepended to it. For a, A, e, E, f, F, g, and G conversions, the result will always contain a decimal point, even if no digits follow it (normally, a decimal point appears in the results of those conversions only if a digit follows). For g and G conversions, trailing zeros are not removed from the result as they would otherwise be. For other conversions, the result is undefined.

The value should be zero padded. For d, i, o, u, x, X, a, A, e, E, f, F, g, and G conversions, the converted value is padded on the left with zeros rather than blanks. If the and flags both appear, the flag is ignored. If a precision is given with a numeric conversion (d, i, o, u, x, and X), the flag is ignored. For other conversions, the behavior is undefined.

The converted value is to be left adjusted on the field boundary. (The default is right justification.) Except for n conversions, the converted value is padded on the right with blanks, rather than on the left with blanks or zeros. A overrides a if both are given.

(a space) A blank should be left before a positive number (or empty string) produced by a signed conversion.

A sign (+ or -) should always be placed before a number produced by a signed conversion. By default a sign is used only for negative numbers. A + overrides a space if both are used. The five flag characters above are defined in the C standard. The SUSv2 specifies one further flag character.

For decimal conversion (i, d, u, f, F, g, G) the output is to be grouped with thousands’ grouping characters if the locale information indicates any. Note that many versions of gcc(1) cannot parse this option and will issue a warning. SUSv2 does not include %’F. glibc 2.2 adds one further flag character. I

For decimal integer conversion (i, d, u) the output uses the locale’s alternative output digits, if any. For example, since glibc 2.2.3 this will give Arabic-Indic digits in the Persian («fa_IR») locale.

The field width An optional decimal digit string (with nonzero first digit) specifying a minimum field width. If the converted value has fewer characters than the field width, it will be padded with spaces on the left (or right, if the left-adjustment flag has been given). Instead of a decimal digit string one may write «*» or «*m$» (for some decimal integer m) to specify that the field width is given in the next argument, or in the m-th argument, respectively, which must be of type int. A negative field width is taken as a ‘-‘ flag followed by a positive field width. In no case does a nonexistent or small field width cause truncation of a field; if the result of a conversion is wider than the field width, the field is expanded to contain the conversion result.

The precision An optional precision, in the form of a period (‘.’) followed by an optional decimal digit string. Instead of a decimal digit string one may write «*» or «*m$» (for some decimal integer m) to specify that the precision is given in the next argument, or in the m-th argument, respectively, which must be of type int. If the precision is given as just ‘.’, or the precision is negative, the precision is taken to be zero. This gives the minimum number of digits to appear for d, i, o, u, x, and X conversions, the number of digits to appear after the radix character for a, A, e, E, f, and F conversions, the maximum number of significant digits for g and G conversions, or the maximum number of characters to be printed from a string for s and S conversions.

The length modifier Here, «integer conversion» stands for d, i, o, u, x, or X conversion. hh

A following integer conversion corresponds to a signed char or unsigned char argument, or a following n conversion corresponds to a pointer to a signed char argument.

A following integer conversion corresponds to a short int or unsigned short int argument, or a following n conversion corresponds to a pointer to a short int argument.

(ell) A following integer conversion corresponds to a long int or unsigned long int argument, or a following n conversion corresponds to a pointer to a long int argument, or a following c conversion corresponds to a wint_t argument, or a following s conversion corresponds to a pointer to wchar_t argument.


(ell-ell). A following integer conversion corresponds to a long long int or unsigned long long int argument, or a following n conversion corresponds to a pointer to a long long int argument.

A following a, A, e, E, f, F, g, or G conversion corresponds to a long double argument. (C99 allows %LF, but SUSv2 does not.)

(«quad». 4.4BSD and Linux libc5 only. Don’t use.) This is a synonym for ll.

A following integer conversion corresponds to an intmax_t or uintmax_t argument.

A following integer conversion corresponds to a size_t or ssize_t argument. (Linux libc5 has Z with this meaning. Don’t use it.)

A following integer conversion corresponds to a ptrdiff_t argument. The SUSv2 only knows about the length modifiers h (in hd, hi, ho, hx, hX, hn) and l (in ld, li, lo, lx, lX, ln, lc, ls) and L (in Le, LE, Lf, Lg, LG).

The conversion specifier A character that specifies the type of conversion to be applied. The conversion specifiers and their meanings are: d, i

The int argument is converted to signed decimal notation. The precision, if any, gives the minimum number of digits that must appear; if the converted value requires fewer digits, it is padded on the left with zeros. The default precision is 1. When 0 is printed with an explicit precision 0, the output is empty. o, u, x, X The unsigned int argument is converted to unsigned octal (o), unsigned decimal (u), or unsigned hexadecimal (x and X) notation. The letters abcdef are used for x conversions; the letters ABCDEF are used for X conversions. The precision, if any, gives the minimum number of digits that must appear; if the converted value requires fewer digits, it is padded on the left with zeros. The default precision is 1. When 0 is printed with an explicit precision 0, the output is empty. e, E

The double argument is rounded and converted in the style [-]d.dddeВ±dd where there is one digit before the decimal-point character and the number of digits after it is equal to the precision; if the precision is missing, it is taken as 6; if the precision is zero, no decimal-point character appears. An E conversion uses the letter E (rather than e) to introduce the exponent. The exponent always contains at least two digits; if the value is zero, the exponent is 00.

The double argument is rounded and converted to decimal notation in the style [-]ddd.ddd, where the number of digits after the decimal-point character is equal to the precision specification. If the precision is missing, it is taken as 6; if the precision is explicitly zero, no decimal-point character appears. If a decimal point appears, at least one digit appears before it. (The SUSv2 does not know about F and says that character string representations for infinity and NaN may be made available. The C99 standard specifies «[-]inf» or «[-]infinity» for infinity, and a string starting with «nan» for NaN, in the case of f conversion, and «[-]INF» or «[-]INFINITY» or «NAN*» in the case of F conversion.) g, G

The double argument is converted in style f or e (or F or E for G conversions). The precision specifies the number of significant digits. If the precision is missing, 6 digits are given; if the precision is zero, it is treated as 1. Style e is used if the exponent from its conversion is less than -4 or greater than or equal to the precision. Trailing zeros are removed from the fractional part of the result; a decimal point appears only if it is followed by at least one digit.

(C99; not in SUSv2) For a conversion, the double argument is converted to hexadecimal notation (using the letters abcdef) in the style [-]0xh.hhhhpВ±; for A conversion the prefix 0X, the letters ABCDEF, and the exponent separator P is used. There is one hexadecimal digit before the decimal point, and the number of digits after it is equal to the precision. The default precision suffices for an exact representation of the value if an exact representation in base 2 exists and otherwise is sufficiently large to distinguish values of type double. The digit before the decimal point is unspecified for nonnormalized numbers, and nonzero but otherwise unspecified for normalized numbers.

If no l modifier is present, the int argument is converted to an unsigned char, and the resulting character is written. If an l modifier is present, the wint_t (wide character) argument is converted to a multibyte sequence by a call to the wcrtomb(3) function, with a conversion state starting in the initial state, and the resulting multibyte string is written.

If no l modifier is present: The const char * argument is expected to be a pointer to an array of character type (pointer to a string). Characters from the array are written up to (but not including) a terminating null byte (‘\0’); if a precision is specified, no more than the number specified are written. If a precision is given, no null byte need be present; if the precision is not specified, or is greater than the size of the array, the array must contain a terminating null byte. If an l modifier is present: The const wchar_t * argument is expected to be a pointer to an array of wide characters. Wide characters from the array are converted to multibyte characters (each by a call to the wcrtomb(3) function, with a conversion state starting in the initial state before the first wide character), up to and including a terminating null wide character. The resulting multibyte characters are written up to (but not including) the terminating null byte. If a precision is specified, no more bytes than the number specified are written, but no partial multibyte characters are written. Note that the precision determines the number of bytes written, not the number of wide characters or screen positions. The array must contain a terminating null wide character, unless a precision is given and it is so small that the number of bytes written exceeds it before the end of the array is reached. C

(Not in C99, but in SUSv2.) Synonym for lc. Don’t use.

(Not in C99, but in SUSv2.) Synonym for ls. Don’t use.

The void * pointer argument is printed in hexadecimal (as if by %#x or %#lx).

The number of characters written so far is stored into the integer indicated by the int * (or variant) pointer argument. No argument is converted.

(Glibc extension.) Print output of strerror(errno). No argument is required.

A ‘%’ is written. No argument is converted. The complete conversion specification is ‘%%’.

Conforming To

The fprintf(), printf(), sprintf(), vprintf(), vfprintf(), and vsprintf() functions conform to C89 and C99. The snprintf() and vsnprintf() functions conform to C99.

Concerning the return value of snprintf(), SUSv2 and C99 contradict each other: when snprintf() is called with size=0 then SUSv2 stipulates an unspecified return value less than 1, while C99 allows str to be NULL in this case, and gives the return value (as always) as the number of characters that would have been written in case the output string has been large enough.

Linux libc4 knows about the five C standard flags. It knows about the length modifiers h, l, L, and the conversions c, d, e, E, f, F, g, G, i, n, o, p, s, u, x, and X, where F is a synonym for f. Additionally, it accepts D, O, and U as synonyms for ld, lo, and lu. (This is bad, and caused serious bugs later, when support for %D disappeared.) No locale-dependent radix character, no thousands’ separator, no NaN or infinity, no «%m$» and «*m$».

Linux libc5 knows about the five C standard flags and the ‘ flag, locale, «%m$» and «*m$». It knows about the length modifiers h, l, L, Z, and q, but accepts L and q both for long double and for long long int (this is a bug). It no longer recognizes F, D, O, and U, but adds the conversion character m, which outputs strerror(errno).

glibc 2.0 adds conversion characters C and S.

glibc 2.1 adds length modifiers hh, j, t, and z and conversion characters a and A.

glibc 2.2 adds the conversion character F with C99 semantics, and the flag character I.

Notes

Some programs imprudently rely on code such as the following

sprintf(buf, «%s some further text», buf);

to append text to buf. However, the standards explicitly note that the results are undefined if source and destination buffers overlap when calling sprintf(), snprintf(), vsprintf(), and vsnprintf(). Depending on the version of gcc(1) used, and the compiler options employed, calls such as the above will not produce the expected results.

The glibc implementation of the functions snprintf() and vsnprintf() conforms to the C99 standard, that is, behaves as described above, since glibc version 2.1. Until glibc 2.0.6 they would return -1 when the output was truncated.

Because sprintf() and vsprintf() assume an arbitrarily long string, callers must be careful not to overflow the actual space; this is often impossible to assure. Note that the length of the strings produced is locale-dependent and difficult to predict. Use snprintf() and vsnprintf() instead (or asprintf(3) and vasprintf(3)).

Linux libc4.[45] does not have a snprintf(), but provides a libbsd that contains an snprintf() equivalent to sprintf(), that is, one that ignores the size argument. Thus, the use of snprintf() with early libc4 leads to serious security problems.

Code such as printf(foo); often indicates a bug, since foo may contain a % character. If foo comes from untrusted user input, it may contain %n, causing the printf() call to write to memory and creating a security hole.

Example

To print Pi to five decimal places: To print a date and time in the form «Sunday, July 3, 10:02», where weekday and month are pointers to strings: Many countries use the day-month-year order. Hence, an internationalized version must be able to print the arguments in an order specified by the format: where format depends on locale, and may permute the arguments. With the value: one might obtain «Sonntag, 3. Juli, 10:02».

To allocate a sufficiently large string and print into it (code correct for both glibc 2.0 and glibc 2.1):

If truncation occurs in glibc versions prior to 2.0.6, this is treated as an error instead of being handled gracefully.

Vfprintf vsprintf

The following differences exist for these functions:

In the SunOS 4.1 release, sprintf returns a pointer to its first argument; in the SunOS 5.x release, it returns the count of characters printed.

In the SunOS 4.1 release, printf and vprintf always return the number of characters printed or EOF, while the SunOS 5.x version returns only EOF in case of an error.

fprintf and vfprintf share the same differences as printf and vprintf (above); additionally, the buffering scheme for these routines is different for the SunOS 5.x and SunOS 4.1 release.

The SunOS 5.x returns the generated number shifted to the right by 16 and anded with 0x7fff, while the SunOS 4.1 release returns the generated number anded with 0x7fffffff.

re_comp and re_exec

These routines are similar to the regcmp(3G) and regex routines. re_comp compiles the regular expression in a string, returning 0 if it compiled successfully or returning a pointer to an error message if it did not. regcmp, on the other hand, returns a pointer to the compiled form or NULL if given an incorrect argument. re_exec returns 1 if the string s matches the last compiled regular expression, 0 if it fails to match, and -1 if the regular expression is invalid. regex, however, returns NULL on failure or a pointer to the next unmatched character on success.

Reads a directory entry. The SunOS 4.1 release readdir routine returns a pointer to struct direct, while the SunOS 5.x version returns a pointer to struct dirent.

setbuffer and setlinebuf

These routines are used to provide control over the buffering used for input and output.

Similar to setbuf(3S) and setvbuf, setbuffer may be used after a stream has been opened but before it is read or written. It uses the array pointer instead of an automatically allocated buffer. If the buffer is the NULL pointer, input/output is completely unbuffered.

setlinebuf is used to change the buffering on a stream from block buffered or unbuffered to line buffered. It can be used at any time that the file descriptor is active.

These routines are not available in the default SunOS 5.x release.

The SunOS 5.x version returns the «unslept» amount, that is, the requested time minus the time actually slept. The SunOS 4.1 version does not return anything.

An array containing a message string for each SunOS 4.1 signal number.

The SunOS 4.1 version returns 0 when successful. The SunOS 5.x version returns the elapsed real time in clock ticks per second from an arbitrary point in the past.

ualarm and usleep

Simplified interfaces to the system calls handling high-resolution timers. These routines are not available in the default SunOS 5.x release.

Difference between fprintf, printf and sprintf?

Can anyone explain in simple English about the differences between printf , fprintf , and sprintf with examples?

What stream is it in?

I’m really confused between the three of these while reading about «File Handling in C».

8 Answers 8

In C, a «stream» is an abstraction; from the program’s perspective it is simply a producer (input stream) or consumer (output stream) of bytes. It can correspond to a file on disk, to a pipe, to your terminal, or to some other device such as a printer or tty. The FILE type contains information about the stream. Normally, you don’t mess with a FILE object’s contents directly, you just pass a pointer to it to the various I/O routines.


There are three standard streams: stdin is a pointer to the standard input stream, stdout is a pointer to the standard output stream, and stderr is a pointer to the standard error output stream. In an interactive session, the three usually refer to your console, although you can redirect them to point to other files or devices:

In this example, stdin now points to inputfile.dat , stdout points to output.txt , and stderr points to errors.txt .

fprintf writes formatted text to the output stream you specify.

printf is equivalent to writing fprintf(stdout, . ) and writes formatted text to wherever the standard output stream is currently pointing.

sprintf writes formatted text to an array of char , as opposed to a stream.

Differences between printf, print, echo, sprintf,vprintf, vfprintf, vsprintf in PHP

WTF. Look make no mistake about it, I do like PHP but why do we have so many different ways to output strings. It is confusing especially nowadays when a programmer is expecting to know more than one language. As I move from one language to another i keep forgetting the nuances between all of them.

I wish they could just create just one construct and then we could just use flags to access specific functionalities.

So what are the differences between printf, print, echo, sprintf,vprintf, vfprintf and vsprintf in PHP ?

According to php.net here are the differences. At least they are all on one page for quick reference.

print :

  • Output a string onto the screen – Also “print” is not a real function so you are welcome to remove the parentheses.
  • Because this is a language construct and not a function, it cannot be called using variable functions
  • Always returns a 1

echo :

  • Output one or more strings
  • Also not a real function but a language construct
  • Does not returns anything so its cannot behave as a function.

printf :

  • Output a formatted string
  • Returns the length of the outputted string

sprintf:

  • Returns a formatted string
  • Returns a string produced according to the formatting string format

vprintf:

  • Returns a formatted string
  • Operates as printf() but accepts an array of arguments, rather than a variable number of arguments.
  • Display array values as a formatted string according to format

vsprintf:

  • Display array values as a formatted string according to format
  • Operates as sprintf() but accepts an array of arguments, rather than a variable number of argument

vfprintf :

  • Write a formatted string to a stream
  • Write a string produced according to format to the stream resource specified by handle
  • Returns the length of the outputted string

Самые частые грабли при использовании printf в программах под микроконтроллеры

Время от времени в моих проектах приходится применять printf в связке с последовательным портом (UART или абстракция над USB, имитирующая последовательный порт). И, как обычно, времени между его применениями проходит много и я успеваю напрочь забыть все нюансы, которые требуется учитывать, чтобы он нормально работал в крупном проекте.

В данной статье я собрал свой собственный топ нюансов, которые возникают при использовании printf в программах под микроконтроллеры, сортированный по очевидности от самых очевидных к полностью неочевидным.

Краткое введение

По сути для того, чтобы использовать printf в программах под микроконтроллеры, достаточно:

  • подключить заголовочный файл в коде проекта;
  • переопределить системную функцию _write на вывод в последовательный порт;
  • описать заглушки системных вызовов, которые требует компоновщик (_fork, _wait и прочие);
  • использовать printf вызов в проекте.

На деле же, не все так просто.

Описать нужно все заглушки, а не только используемые

Наличие кучи неопределенных ссылок при компоновке проекта по началу удивляет, но немного почитав, становится понятно, что и для чего. Во всех своих проектах подключаю вот этот субмодуль. Таким образом, в основном проекте я переопределяю только нужные мне методы (только _write в данном случае), а остальные остаются неизменными.

Важно отметить, что все заглушки должны быть C функциями. Не C++ (или обернутые в extern «C»). Иначе компоновка пройдет неудачно (помним про изменение имен при сборке у G++).

В _write приходит по 1 символу

Несмотря на то, что в прототипе метода _write есть аргумент, передающий длину выводимого сообщения, он имеет значение 1 (на самом деле мы сами сделаем так, что он всегда будет 1, но об этом далее).

В интернете часто можно видеть вот такую реализацию этого метода:

У такой реализации есть следующие недостатки:

  • низкая производительность;
  • потоковая незащищенность;
  • невозможность использовать последовательный порт для других целей;

Низкая производительность

Здесь за непосредственную отправку с использованием dma отвечает объект класса uart — uart_1. Объект использует методы FreeRTOS для блокировки стороннего доступа к объекту на момент отправки данных из буфера (взятие и возвращение mutex-а). Таким образом, никто не может воспользоваться объектом uart-а во время отправки из другого потока.
Немного ссылок:

  • код функции _write в составе реального проекта здесь
  • интерфейс класса uart здесь
  • реализация интерфейса класса uart под stm32f4 здесь и здесь
  • создание экземпляра класса uart в составе проекта здесь

Потоковая незащищенность

Данная реализация так же остается потока незащищенной, поскольку никто не мешает в соседнем потоке FreeRTOS начать отправку в printf другой строки и тем самым перетереть отправляемый в данный момент буфер (mutex внутри uart-а защищает объект от использования в разных потоках, но не передаваемые им данные). В случае, если есть риск, что будет вызван printf другого потока, то требуется реализовать объект-прослойку, который будет блокировать доступ к printf целиком. В моем конкретном случае с printf взаимодействует лишь один поток, поэтому дополнительные усложнения лишь уменьшат производительность (постоянное взятие и отпуск mutex-а внутри прослойки).

Невозможность использовать последовательный порт для других целей

Поскольку мы производим отправку только после того, как будет принята строка целиком (или заполнен буфер), то вместо объекта uart можно вызывать метод-конвертер в какой-либо интерфейс верхнего уровня для последующей пересылки пакета (например доставка с гарантией по протоколу передачи схожим с пакетами транзакций modbus). Это позволит использовать один uart как для вывода отладочной информации, так и, например, для взаимодействия пользователя с консолью управления (если таковая имеется в устройстве). Достаточно будет написать декомпрессор на стороне получателя.

По умолчанию не работает вывод float

Если вы используете newlib-nano, то по умолчанию printf (а так же все их производные по типу sprintf/snprintf… и прочие) не поддерживают вывод float значений. Это легко решается добавлением в проект следующих флагов компоновщика.

Посмотреть полный перечень флагов можно здесь.

Программа зависает где-то в недрах printf

Это еще одна недоработка флагов компоновщика. Чтобы прошивка была скомпонована с нужной версией библиотеки нужно явно указать параметры процессора.

Посмотреть полный перечень флагов можно также здесь.

printf заставляет микроконтроллер попасть в hard fault

Проблемы со стеком

Эта проблема действительно проявляет себя при использовании FreeRTOS или любой другой ОС. Проблема заключается в использовании буфера. В первом пункте было сказано, что в _write приходит по 1 байту. Для того, чтобы это произошло, нужно в коде, перед первым использованием printf запретить использование буферизации.

Из описания функции следует, что можно установить так же и одно из следующих значений:

Однако это может привести к переполнению стека задачи (или прерывания, если вы вдруг очень нехороший человек, который вызывает printf из прерываний).

Чисто технически, можно очень аккуратно расставить для каждого потока стеки, однако такой подход требует тщательного планирования и ошибки, которые он несет в себе, трудно отловить. Куда более простым решением является прием по одному байту, складирование в собственный буфер с последующим выводом в требуемом формате, разобранный ранее.

Проблемы с _sbrk

Эта проблема была лично для меня самой неявной. И так, что мы знаем о _sbrk?

  • очередная заглушка, которую требуется реализовать для поддеркжи немалой части стандартных библиотек;
  • требуется для выделения памяти в куче;
  • используется всякими библиотечными методами по типу malloc, free.



Лично я в своих проектах в 95% случаев использую FreeRTOS с переопределенными методами new/delete/malloc, использующими кучу FreeRTOS. Так что когда я выделяю память, то уверен, что выделение идет в куче FreeRTOS, которая занимает заранее известное количество памяти в bss области. Посмотреть на прослойку можно здесь. Так что, чисто технически, никакой проблемы быть не должно. Функция просто не должна вызываться. Но давайте подумаем, если она вызовится, то где она попытается взять память?

Вспомним разметку оперативной памяти «классического» проекта под микроконтроллеры:

  • .data;
  • .bss;
  • пустое пространство;
  • начальный стек.

В data у нас начальные данные глобальных объектов (переменные, структуры и прочие глоабльные поля проекта). В bss — глобальные поля, имеющие начальное нулевое значение и, внимательно, куча FreeRTOS. Она представляет из себя просто массив в памяти. с которым потом работают методы из файла heap_x.c. Далее идет пустое пространство, после которого (вернее сказать с конца) располагается стек. Т.к. в моем проекте используется FreeRTOS, то данный стек используется только до момента запуска планировщика. И, таким образом, его использование, в большинстве случаев, ограничивается коллобайтом (на деле обычно 100 байт предел).

Но где же тогда выделяется память с помощью _sbrk? Взглянем на то, какие переменные она использует из linker script-а.

Теперь найдем их в linker script-е (мой скрипт немного отличается от того, который предоставляет st, однако эта часть там примерно такая же):

То есть он использует память между стеком (1 кб от 0x20020000 вниз при 128 кб RAM) и bss.

Разобрались. Но ведь у нес есть переопределние методов malloc, free и прочих. Использовать _sbrk ведь ведь не обязательно? Как оказалось, обязательно. Причем этот метод использует не printf, а метод для установки режима буферизации — setvbuf (вернее сказать _malloc_r, который в библиотеке объявлен не как слабая функция. В отличии от malloc, который можно легко заменить).

Так как я был уверен, что sbrk не используется, расположил кучу FreeRTOS (секцию bss) вплотную к стеку (поскольку точно знал, что стека используется раз в 10 меньше, чем требуется).

Решения проблемы 3:

  • сделать некоторый отступ между bss и стеком;
  • переопределить _malloc_r, чтобы _sbrk не вызывался (отделить от библиотеки один метод);
  • переписать sbrk через malloc и free.

Я остановился на первом варианте, поскольку безопасно заменить стандартный _malloc_r (который находится внутри libg_nano.a(lib_a-nano-mallocr.o)) мне не удалось (метод не объявлен как __attribute__ ((weak)), а исключить только единственную функцию из биюлиотеки при компановке мне не удалось). Переписывать же sbrk ради одного вызова — очень не хотелось.

Конечным решением стало выделение отдельных разделов в RAM под начальный стек и _sbrk. Это гарантирует, что на этапе компановки секции не налезут друг на друга. Внутри sbrk так же есть проверка на выход за пределы секции. Пришлось внести небольшую правку, чтобы при детектировании перехода за границу поток зависал в while цикле (посколько использование sbrk происходит только на начальном этапе инициализации и должно быть обработано на этапе отладки устройства).

Посмотреть на mem.ld и section.ld можно в моем проекте-песочнице в этом коммите.

UPD 12.07.2020: поправил перечень флагов для работы printf с float значениями. Поправил ссылку на рабочий CMakeLists с поправленными флагами компиляции и компоновки (были нюансы с тем, что флаги нужно перечислять по одному и через ";", при этом на одной все строке или на разных — не играет роли).

iOS Manual Pages

  • Table of Contents
  • Download Sample Code
ADC Home>Reference Library> Reference > Mac OS X > Mac OS X Man Pages

This document is a Mac OS X manual page. Manual pages are a command-line technology for providing documentation. You can view these manual pages locally using the man(1) command. These manual pages come from many different sources, and thus, have a variety of writing styles.

For more information about the manual page format, see the manual page for manpages(5).

Sending feedback…

We’re sorry, an error has occurred.

Please try submitting your feedback later.

Thank you for providing feedback!

Your input helps improve our developer documentation.

Vfprintf vsprintf

Самая актуальная документация по Visual Studio 2020: Документация по Visual Studio 2020.

Записывают форматированные выходные данные с помощью указателя на список аргументов. Доступны более безопасные версии этих функций; в разделе vsprintf_s _vsprintf_s_l, vswprintf_s, _vswprintf_s_l.

Параметры

buffer
Место хранения выходных данных.

count
Максимальное количество символов для хранения в UNICODE версия этой функции.

format
Спецификация формата.

argptr
Указатель на список аргументов.

locale
Используемый языковой стандарт.

vsprintf и vswprintf возвращают число записанных символов, не включая завершающий символ null или отрицательное значение, если произошла ошибка вывода. Если buffer или format является пустым указателем, эти функции вызывают обработчик недопустимого параметра, как описано в разделе проверки параметров. Если разрешается продолжать выполнение, эти функции возвращают -1 и задают errno значение EINVAL .

Сведения об этих и других кодах ошибок см. в разделе _doserrno, errno, _sys_errlist и _sys_nerr.

Каждая из этих функций принимает указатель на список аргументов и затем форматирует и записывает заданных данных в памяти, на который указывает buffer .

Версии этих функций с суффиксом _l идентичны за исключением того, что они используют переданный параметр языкового стандарта вместо языкового стандарта текущего потока.

С помощью vsprintf , здесь возможности ограничить количество символов записывается, которой означает, что код, с помощью этой функции уязвимо к переполнению буфера. Используйте _vsnprintf вместо, или вызвать _vscprintf помогает определить, требуется размер буфера. Кроме того, убедитесь, что format не является строкой, определяемой пользователем. Дополнительные сведения см. в разделе Как избежать переполнения буфера.

vswprintf соответствует стандарту ISO C, который требует указания второго параметра ( count ) типа size_t . Для принудительного использования старого поведения нестандартные, определить _CRT_NON_CONFORMING_SWPRINTFS. старое поведение может оказаться в будущих версиях, поэтому код должен изменены для использования нового соответствующее поведение.

В C++ эти функции имеют шаблонные перегрузки, которые вызывают более новые и безопасные аналоги этих функций. Дополнительные сведения см. в разделе Secure Template Overloads.

Универсальное текстовое сопоставление функций

Важно
Подпрограмма TCHAR.H _UNICODE и _MBCS не определены _MBCS определено _UNICODE определено
_vstprintf vsprintf vsprintf vswprintf
_vstprintf_l _vsprintf_l _vsprintf_l _vswprintf_l
Подпрограмма Обязательный заголовок Необязательные заголовки
vsprintf , _vsprintf_l и *
vswprintf , _vswprintf_l или и *

*Необходим для совместимости UNIX V.

Дополнительные сведения о совместимости см. в разделе Совместимость во введении.

Vfprintf vsprintf

vfprintf, vprintf, vsnprintf, vsprintf - format output of a stdarg argument list

SYNOPSIS

int vfprintf(FILE *restrict stream, const char *restrict format,
va_list
ap);
int vprintf(const char *restrict
format, va_list ap);
int vsnprintf(char *restrict
s, size_t n, const char *restrict format,
va_list
ap);
int vsprintf(char *restrict
s, const char *restrict format, va_list ap);

DESCRIPTION

The vprintf(), vfprintf(), vsnprintf(), and vsprintf() functions shall be equivalent to printf(), fprintf(), snprintf(), and sprintf() respectively, except that instead of being called with a variable number of arguments, they are called with an argument list as defined by .

These functions shall not invoke the va_end macro. As these functions invoke the va_arg macro, the value of ap after the return is unspecified.

RETURN VALUE

ERRORS

EXAMPLES

APPLICATION USAGE

Applications using these functions should call va_end(ap) afterwards to clean up.

RATIONALE

FUTURE DIRECTIONS

SEE ALSO

fprintf() , the Base Definitions volume of IEEE Std 1003.1-2001, ,

CHANGE HISTORY

First released in Issue 1. Derived from Issue 1 of the SVID.

Issue 5

Issue 6

The vfprintf(), vprintf(), vsnprintf(), and vsprintf() functions are updated for alignment with the ISO/IEC 9899:1999 standard.

Илон Маск рекомендует:  Что такое код closemetafile
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL