Я пытаюсь понять поведение %d
кодирование в параметризованном парсере строк terminfo. Соответствующая страница руководства Вот и заявляет, что —
%[[:]flags][width[.precision]][doxXs]
as in printf, flags are [-+#] and space. Use a ":" to allow the
next character to be a "-" flag, avoiding interpreting "%-" as an
operator.
но ничего не говорит о том, откуда следует печатать значения и что делать с крайними случаями. Они из стека или из аргументов, передаваемых в параметризованную строку? Также, что происходит, когда передаются дополнительные аргументы (не равно %d
в параметризованной строке) или дополнительно %d
присутствуют (неверная параметризованная строка?)? Это неопределенное поведение или реализация определяется или определяется по определению где-то?
Я попытался проверить некоторые случаи, вручную написав несколько допустимых и недопустимых строк и проверив выходные данные, но все до сих пор немного противоречиво, поэтому я не могу увидеть здесь образец —
#include <iostream>
#include <curses.h>
#include <term.h>
using namespace std;
int main() {
// single %d prints single argument
// => 2
auto res = tparm("[%d]", 2);
cout << res << endl;
// single %d prints single argument and ignores additional
// => 2
res = tparm("[%d]", 2, 3, 4);
cout << res << endl;
// multiple %d prints 0 for absent additional arguments
// => 2-0-0-0
res = tparm("[%d-%d-%d-%d]", 2);
cout << res << endl;
// multiple %d prints with equal number of arguments prints
// first two correctly and rest 0
// => 2-3-0-0-0
res = tparm("[%d-%d-%d-%d-%d]", 2,3,4,5,6);
cout << res << endl;
// single value pushed to stack prints from stack
// => 2
res = tparm("[%p1%d]", 2);
cout << res << endl;
// single value pushed to stack prints from stack and ignores extra arguments
// => 2
res = tparm("[%p1%d]", 2,3,4);
cout << res << endl;
// single value pushed to stack prints from stack and additional prints are 0
// if no arguments are provided
// => 2-0-0
res = tparm("[%p1%d-%d-%d]", 2);
cout << res << endl;
// single value pushed to stack prints from stack and additional prints 0
// even if equal arguments are provided
// => 2-0-0
res = tparm("[%p1%d-%d-%d]", 2,3,4);
cout << res << endl;
// single value pushed to stack prints from stack after pop()?
// => 100-<garbage>
res = tparm("[%p1%d-%c]", 100);
cout << res << endl;
// pushed to stack via {} and equal arguments provided, prints all
// => 2-1-100-200
res = tparm("[%{1}%{2}%d-%d-%d-%d]", 100, 200);
cout << res << endl;
// pushed to stack via {} and %p1 equal arguments provided
// prints only stack and rest 0
// => 100-2-1-0
res = tparm("[%{1}%{2}%p1%d-%d-%d-%d]", 100, 200);
cout << res << endl;
}
Одна из проблем, отмеченная в ваших примерах, заключается в том, что они проявляют неопределенное поведение. определенный поведение для terminfo использует явные токены параметров, такие как %p1
передать push-параметры в стек, где они могут быть вытолкнуты такими операторами, как %d
, Не имея этого, вы полагаетесь на обходные пути ncurses для termcap (у которого нет токенов параметров), и от случая к случаю, что сделает выражение вроде
res = tparm("[%d-%d-%d-%d]", 2);
попытка прочитать больше одного параметр из списка параметров. Ваш пример дает один, так что вы в царстве Язык C неопределенное поведение (то есть неправильное количество параметров в списке аргументов переменной длины). Если ваш звонок передал дополнительные параметры, то может быть быть в порядке (см. например Visual C принимает неверное количество аргументов?), но с меньшим, результатом может быть доступ к памяти, которая вне список параметров.
Отвечая на комментарий:
ncurses позволяет использовать в стиле termcap %d
без %p1
, Но он подсчитывает количество параметров, составляя список из них перед выполнением фактической замены. Поскольку он обрабатывает их как список аргументов переменной длины, он не может определить, передало ли ваше приложение неверное количество параметров для данной строки.
Дальнейшее чтение:
tiparm
, tparm
, tputs
, и т.д., — ругает интерфейсы к базе данных terminfotparm
, например.,/ * * Анализ строки, чтобы увидеть, сколько параметров нам нужно из списка varargs, * и каковы их типы. Мы будем принимать строковые параметры, только если они * отображаются в формате% l или% s после явной ссылки на параметр (например, *% p2% s). Все остальные параметры являются числами. * * 'number' грубо подсчитывает количество попсов, которые мы видим в строке, и * 'popcount' показывает самый высокий номер параметра в строке. Мы бы хотели * просто использовать последний счетчик, но если мы читаем строки termcap, то есть * могут быть случаи, когда мы не можем видеть явные номера параметров. * /
а также такие функции, как tc_BUMP которые учитывают отсутствие токенов параметров в termcap.
Других решений пока нет …