Что такое код vsprintf

Содержание

Что такое код vsprintf

(PHP 3, PHP 4, PHP 5)

sprintf — Возвращает отформатированную строку

Описание string sprintf ( string format [, mixed args] )

Возвращает строку, созданную с использованием строки формата format .

Строка формата состоит из директив: обычных символов (за исключением % ), которые копируются в результирующую строку, и описатели преобразований , каждый из которых заменяется на один из параметров. Это относится также к fprintf() , sprintf() и printf() .

Каждый описатель прреобразований состоит из знака процента ( % ), за которым следует один или более дополнительных элементов (в том порядке, в котором они здесь перечислены):

Необязательный описатель заполнения , который определяет, какой символ будет использоваться для дополнения результата до необходимой длины. Это может быть пробел или 0 . По умолчанию используется пробел. Альтернативный символ может быть указан с помощью ‘ . См. примеры ниже.

Необязательный описатель выравнивания , определяющий выранивание влево или вправо. По умолчанию выравнивается вправо, — используется для выравнивания влево.

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

Необязательный описатель точности , определяющий, сколько десятичных разрядов отображать для чисел с плавающей точкой. Имеет смысл только для числовых данных типа float . (Для форматирования чисел удобно также использовать функцию number_format() .)

Описатель типа , определяющий, как трактовать тип данных аргумента. Допустимые типы:

% — символ процента. Аргумент не используется.
b — аргумент трактуется как целое и выводится в виде двоичного числа.
c — аргумент трактуется как целое и выводится в виде символа с соответствующим кодом ASCII.
d — аргумент трактуется как целое и выводится в виде десятичного числа со знаком.
e — аргумент трактуется как float и выводится в научной нотации (например 1.2e+2).
u — аргумент трактуется как целое и выводится в виде десятичного числа без знака.
f — аргумент трактуется как float и выводится в виде десятичного числа с плавающей точкой.
o — аргумент трактуется как целое и выводится в виде восьмеричного числа.
s — аргумент трактуется как строка.
x — аргумент трактуется как целое и выводится в виде шестнадцатиричного числа (в нижнем регистре букв).
X — аргумент трактуется как целое и выводится в виде шестнадцатиричного числа (в верхнем регистре букв).

Начиная с PHP 4.0.6 в строке формата поддерживается нумерация и изменение порядка параметров. Например:

Пример 1. Изменение порядка параметров

= «There are %d monkeys in the %s» ;
printf ( $format , $num , $location );
?>

Этот код выведет «There are 5 monkeys in the tree». Теперь представьте, что строка формата содержится в отдельном файле, который потом будет переведен на другой язык, и мы переписываем ее в таком виде:

Пример 2. Изменение порядка параметров

= «The %s contains %d monkeys» ;
printf ( $format , $num , $location );
?>

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

Пример 3. Изменение порядка параметров

= «The %2\$s contains %1\$d monkeys» ;
printf ( $format , $num , $location );
?>

Нумерация аргументов имеет еще одно применение: она позволят вывести один и тот же аргумент несколько раз без передачи функции дополнительных параметров.

Пример 4. Изменение порядка параметров

= «The %2\$s contains %1\$d monkeys.
That’s a nice %2\$s full of %1\$d monkeys.» ;
printf ( $format , $num , $location );
?>

Примеры

Пример 5. sprintf() : заполнение нулями

= sprintf ( «%04d-%02d-%02d» , $year , $month , $day );
?>

Пример 6. sprintf() : форматирование денежных величин

Функция vprintf, vfprintf, vsprintf, vsnprintf

Действия функций vprintf() , vfprintf() , vsprintf() и vsnprintf() эквивалентны действиям функций printf() , fprintf() , sprintf() и snprintf() соответственно, но список аргументов заменяется указателем на список аргументов. Этот указатель должен иметь тип va_list , который определен в заголовке .

Пример

В версии C99 к параметрам buf и format применен квалификатор restrict . Функция vsnprintf() добавлена в версии C99.Данный фрагмент программы иллюстрирует, как нужно вызывать функцию vprintf() . Вызов функции va_start() приводит к созданию указателя на список аргументов переменной длины, причем этот указатель указывает на начало списка аргументов. Этот указатель должен быть использован при вызове функции vprintf() . Вызов функции va_end() очищает указатель на список аргументов переменной длины.

Что такое код vsprintf

Функции vprintf , vfprintf , vsprintf , vsnprintf эквивалентны соответствующим функциям printf , fprintf , sprintf , snprintf , исключая то, что они вызываются с va_list, а не с переменным количеством аргументов. Эти функции не вызывают макрос va_end , и поэтому значение ap после вызова неопределенно. Приложение может позже само вызвать va_end(ap) .

Эти восемь функций выводят данные в соответствии со строкой format , которая определяет, каким образом последующие параметры (или доступные параметры переменной длины из stdarg (3)) преобразуют поток вывода.

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


Структура строки параметров

В некоторых цифровых преобразованиях используется символ десятичной точки или символ тысячной группировки `,’. Текущий символ зависит от переменной LC_NUMERIC. Стандарт POSIX по умолчанию использует символ `.’ и не поддерживает символ группировки. Таким образом, выводит `1234567.89′ в стандарте POSIX, `1234567,89′ в локализации nl_NL и `1.234.567,89′ в локализации da_DK.

Флаги

Пять флагов, описанных выше, определены в стандарте C. В стандарте SUSv2 определен один дополнительный флаг. ‘ При десятичных преобразованиях ( i , d , u , f , g , G ) данные группируются символом тысячной группировки, если информация локализации не указывает на это. Обратите внимание, что многие версии gcc не могут распознать эту опцию и выводят соответствующее предупреждение. SUSv2 не включает %’F.

glibc 2.2 добавит в будущем этот флаг. I При преобразовании целых десятичных чисел ( i , d , u ) вывод использует локальное представление цифр (например арабские цифры). Однако он не включает все локальные определения как определено в outdigits . См. http://sources.redhat.com/ml/libc-alpha/2000-08/msg00230.html

Ширина поля


Точность


Модификаторы длины

SUSv2 располагает информацией только о модификаторах длины h (с hd , hi , ho , hx , hX , hn ); l (с ld , li , lo , lx , lX , ln , lc , ls ) и L (с Le , LE , Lf , Lg , LG ).

Тип преобразования


ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ

Печатает дату и вpемя в фоpме `Sunday, July 3, 10:02′, где weekday и month являются указателями на стpоку:

Многие стpаны используют поpядок отобpажения даты в виде «день, месяц, год». Следовательно, междунаpодная веpсия должна отображать значений в pазличных стандаpтах: где format зависит от локальных установок и может менять аpгументы. Оперируя значениями , можно получить `Воскресенье, 3. Июль, 10:02′.

Указание достаточно большой строки и ее вывод (код корректен для обеих версий: glibc 2.0 и glibc 2.1):

СООТВЕТСТВИЕ СТАНДАРТАМ

Что касается возвpащаемого значения snprintf , то стандаpты SUSv2 и C99 пpотивоpечат дpуг дpугу: когда snprintf вызывается с size =0 , тогда SUSv2 пpедусматpивает неопpеделенную величину возвpата, меньшую единицы, а C99 устанавливает в этом случае str pавной NULL и возвpащает значение (как обычно) в виде числа символов, размер которых в выходной стpоке был бы достаточным.

Библиотека Linux libc4 располагает информацией о пяти стандартных флагах C. Она также знает о модификаторах длины g, l, L и преобразованиях cdeEfFgGinopsuxX, где F является синонимом f. Дополнительно она принимает D, O, U как синонимы ld, lo, lu. (Это плохо и привело позже к серьезной ошибке, когда исчезла поддержка %D.) Зависимые от локали символы системы исчисления, разделители тысяч, бесконечность, %m$ и *m$ не поддерживаются.

Библиотека Linux libc5 располагает информацией о пяти стандартных флагах C и флагах локализации, %m$ и *m$, о модификторах длины h,l,L,Z,q, но соотносит типы L и q с long double и сверхдлинным целым (это ошибка). Данная библиотека больше не распознает FDOU, но содержит новый символ преобразования m , который выводит strerror(errno) .

glibc 2.0 сейчас поддерживает символы C и S.

К glibc 2.1 добавлены модификаторы длины hh,j,t,z и символы преобразования a,A. В glibs 2.2 добавлены символы преобразования F с семантикой C99 и флаг I.

ПРИМЕЧАНИЯ ПО ИСТОРИИ


НАЙДЕННЫЕ ОШИБКИ

Библиотека Linux libc4.[45] не имеет функции snprintf , однако предоставляет libbsd, содержащую snprintf , эквивалентную sprintf , то есть игнорирующую аргумент size . Таким образом, использование snprintf с ранними версиями libc4 ведет к серьезным проблемам с безопасностью.

Код типа printf( foo ); часто приводит к ошибке, если foo может содержать символ %. Если в foo записан непроверенный ввод пользователя, то содержит foo может содержать %n, и это вызовет запись в память и создание бреши в безопасности.

Некоторые преобразования чисел с плавающей запятой в ранних версиях libc4 приводили к утечкам памяти.

Что такое эквивалент PHP vsprintf () в C?

Я это PHP-код и хочу эквивалент в C по соображениям производительности. Я сделал R & D Google; Я не получил какого-либо решения.

Если вы знаете , сколько аргументов вы передаете, вы просто звоните snprintf(3) . Если вы не знаете, единственным вариантом является использование VARIADIC функцию и вызвать vsnprintf . Это обычно используется в лесозаготовительном коде в виде тонкой оболочки вокруг snprintf . Например:

Если вы на самом деле есть только аргументы в массив, нет никакого способа , чтобы сделать то , что вы хотите использовать стандартный C. Вы можете использовать библиотеки , такие как ffcall , но это не является переносимым для всех систем. Например, вот как вы могли бы сделать это с ffcall:

Если вы знаете строку заранее, можно просто вручную раскатать массив:

Для того, чтобы сделать это в C динамически:

Вы, наверное, могли бы углубиться в ССАГПЗ внутренностей, но это не будет переносимым.

PHP скрипт для создания соответствующего кода (это должно выполняться до компиляции):

Я попробовал этот вариант работает отлично, как получить AttrS как «|» строка с разделителями и патч может я еще больше оптимизировать это, если это возможно, какие-то узкие места с этим.

Существует не прямой аналог PHP вариант vsprintf() в C. Существует функция vsprintf() — которая обычно не используется; безопаснее использовать vsnprintf() вместо этого. Но это не принимает массив символьных указателей, и не какой — либо другой вариант printf() .

У вас есть несколько вариантов. Один изложено @foo Bah является одним из способов сделать это; усложняющий фактор является то, что вы должны выписать 30-70 вариантов, которые некрасиво, если ничего другого.

Я думаю , что в этих обстоятельствах, я бы написать вариант, разобранный строку формата и обрабатывается массив. Он будет копировать буквенные компоненты формата в результате, а затем вызвать либо snprintf() в формате (копирование) элементов из массива при запросе. Хочу отметить , что форматы , такие как %*.*s не вариант в контексте; нет никакого способа , чтобы получить целые значения пропускаются (или, не чистый способ не делать). И нет простого способа поддерживать чередующиеся спецификатор формата , такие как %f и %d . Один интересный вопрос в дизайне интерфейса является «вы передать длину массива и проверить длину и формат, или вы позволяете формат определения длины массива»?

Так как место для ошибок, я хотел бы использовать интерфейс двойной проверки — первый в списке. Предложенный as суффикс для массива строк , конечно. Я вполне готов вести переговоры о количестве и расположении const отборочных на array , но концептуально, это набор указателей , которые функция не собираются менять вообще.

Вопрос по printf, fortify-source, c, c++ &#8211 Отключить использование __sprintf_chk ()

Я наблюдаю, что программа на C ++ использует sprintf где этот sprintf неявно вызывает __sprintf_chk() . This __sprintf_chk() Кажется, чтобы проверить переполнение буфера путем изучения кадров стека.

Интересно, можно ли отключить __sprintf_chk() ?

Это отключит любые sprintf-изменения на основе препроцессора (* только если sprintf был изменен с использованием функционально-подобного макроса, как в случае __sprintf_chk ).

Для gcc есть варианты -fno-stack-protector -fno-mudflap , Может быть также -D_FORTIFY_SOURCE=0 (для любого бойца)

Для Ubuntu и Debian есть страницы со списком функций безопасности:http://wiki.debian.org/Hardening а такжеhttps://wiki.ubuntu.com/Security/Features Некоторые используемые флаги компилятора перечислены здесьhttps://wiki.ubuntu.com/ToolChain/CompilerFlags

PS: то же самое для __fgets_chk __gets_chk __printf_chk __fprintf_chk __vprintf_chk __vfprintf_chk __vsprintf_chk __wmemcpy_chk __wmemmove_chk __wmempcpy_chk __wmemset_chk __wcscpy_chk __wcpcpy_chk __wcsncpy_chk __wcpncpy_chk __wcscat_chk __wcsncat_chk __swprintf_chk __vswprintf_chk __fwprintf_chk __wprintf_chk __vfwprintf_chk __vwprintf_chk __fgetws_chk __wcrtomb_chk __mbsrtowcs_chk __wcsrtombs_chk __mbsnrtowcs_chk __wcsnrtombs_chk __memcpy_chk __memmove_chk __mempcpy_chk __memset_chk __strcpy_chk __strncpy_chk __stpncpy_chk __strcat_chk и некоторые другие

Why use asprintf() instead of sprintf()?

I’m having a hard time understanding why you would need asprintf. Here in the manual it says

The functions asprintf() and vasprintf() are analogs of sprintf(3) and vsprintf(3) , except that they allocate a string large enough to hold the output including the terminating null byte, and return a pointer to it via the first argument. This pointer should be passed to free(3) to release the allocated storage when it is no longer needed.

So here is the example that I’m trying to understand:

What’s the difference if the buffer allocates a string large enough vs saying char* = (string)

2 Answers 2

If you use sprintf() or vsprintf() , you need to allocate a buffer first, and you need to be sure that the buffer is large enough to contain what sprintf writes. Otherwise sprintf() will happily overwrite whatever memory lies beyond the end of the buffer.

. writes the ‘6’ and the terminating null beyond the end of the space allocated to x , either corrupting some other variable, or causing a segmentation fault.

If you’re lucky, it will trample on memory in-between allocated blocks, and will do no harm — this time. This leads to intermittent bugs — the hardest kind to diagnose. It’s good to use a tool like ElectricFence that causes overruns to fail-fast.

A non-malicious user who provides an overlong input, could cause the program to behave in unexpected ways. A malicious user could exploit this as a way to get their own executable code into the system.

One guard against this is to use snprintf() , which truncates the string to the maximum length you supply.

The return value size is the length that would have been written if space was available — not including the terminating null.

In this case, if size is greater than or equal to 5 then you know that truncation occurred — and if you didn’t want truncation, you could allocate a new string and try snprintf() again.

(that’s a pretty naive algorithm, but it illustrates the point)

asprintf() does this in one step for you — calculates the length of the string, allocates that amount of memory, and writes the string into it.

In all cases, once you’ve finished with x you need to release it, or you leak memory:

asprintf() is an implicit malloc() , so you have to check it worked, just as you would with malloc() or any other system call.

Note that asprintf() is part of the GNU and BSD extensions to libc — you can’t be sure it will be available in every C environment. sprintf() and snprintf() are part of the POSIX and C99 standards.

Что такое код 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.

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

Самые частые грабли при использовании 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 с поправленными флагами компиляции и компоновки (были нюансы с тем, что флаги нужно перечислять по одному и через «;», при этом на одной все строке или на разных — не играет роли).

Функция vprintf, vfprintf, vsprintf, vsnprintf

Действия функций vprintf() , vfprintf() , vsprintf() и vsnprintf() эквивалентны действиям функций printf() , fprintf() , sprintf() и snprintf() соответственно, но список аргументов заменяется указателем на список аргументов. Этот указатель должен иметь тип va_list , который определен в заголовке .

В версии C99 к параметрам buf и format применен квалификатор restrict . Функция vsnprintf() добавлена в версии C99.

Пример

Данный фрагмент программы иллюстрирует, как нужно вызывать функцию vprintf() . Вызов функции va_start() приводит к созданию указателя на список аргументов переменной длины, причем этот указатель указывает на начало списка аргументов. Этот указатель должен быть использован при вызове функции vprintf() . Вызов функции va_end() очищает указатель на список аргументов переменной длины.

printf — C ++, функция Wrapper для sprintf_s

после включения banned.h (одного из инструментов безопасности Microsoft) компилятор выдает мне предупреждение, что sprintf() функция небезопасна, и центр MSDN дает мне предложение использовать sprintf_s Так как мой проект кроссплатформенный, я написал оболочку для функции sprintf.

это дает мне ошибку в строке sprintf_s(buffer, sizeof(buffer), format. );

ошибка C2059: синтаксическая ошибка: ‘…’

Кто-нибудь знает, как написать функцию-обертку для sprintf_s() ?

Решение

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

Шаги в основном:

  • включить stdarg заголовок.
  • объявить va_list ,
  • вызов va_start ,
  • позвонить одному из v*printf функции.
  • вызов va_end ,

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

Я склонен игнорировать предложения Microsoft о sprintf быть небезопасным Это небезопасно, только если вы не знаете, что делаете, и это можно сказать о любой инструмент. Если вы хотите стать хорошим программистом C, вы изучите ограничения и недостатки языка.

В том числе тот, где вы используете sizeof на char* , ожидая, что он вернет размер буфера, на который он указывает, а не размер указателя ��

Но если вы хотите быть C ++ разработчик, быть разработчик C ++. Хотя C и C ++ имеют много общего, они не тот же язык. C ++ включает в себя много C, прежде всего, так что вы можете (в основном) взять уже написанный C-код и использовать его в своих приложениях C ++.

Другими словами, если это приложение C ++, используйте std::string а также std::stringstream скорее, чем char массивы и s*printf звонки.

Вы должен писать свой код C ++, как если бы биты C не существовали. В противном случае, вы скорее программист на C +, чем программист на C ++ ��

Илон Маск рекомендует:  Источники бесперебойного питания. Как выбрать ИБП для компьютера
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL