Как работают позиционные аргументы типа "1 $" с printf()?

В man я нахожу

               printf("%*d", width, num);

и

               printf("%2$*1$d", width, num);

эквивалентны.

Но ИМО второй стиль должен быть таким же, как:

               printf("%*d", num, width);

Однако через тестирование кажется, что man прав; почему?

Ответ 1

Соответствующая часть спецификации POSIX printf() определяет это поведение:

Конверсии могут применяться к аргументу n после формата в списке аргументов, а не к следующему неиспользуемому аргументу. В этом случае символ спецификатора преобразования% (см. Ниже) заменяется последовательностью "% n $", где n является десятичным целым в диапазоне [1, {NL_ARGMAX}], что дает положение аргумента в аргументе список. Эта функция обеспечивает определение строк формата, которые выбирают аргументы в порядке, соответствующем определенным языкам (см. Раздел ПРИМЕРЫ).

Формат может содержать либо спецификацию преобразования нумерованных аргументов (то есть "% n $" и "* m $" ), либо ненумерованные параметры преобразования аргументов (то есть% и *), но не оба. Единственным исключением из этого является то, что %% можно смешать с формой "% n $". Результаты смешивания нумерованных и ненумерованных параметров аргумента в строке формата undefined. При использовании спецификаций с нумерованными аргументами, для указания аргумента N th требуется, чтобы все ведущие аргументы от первого до (N-1) -го были указаны в строке формата.

В строках формата, содержащих форму преобразования "% n $", пронумерованные аргументы в списке аргументов можно указать из строки формата столько раз, сколько требуется.

%n$ идентифицирует аргумент, значение которого должно быть напечатано - аргумент 2 в вашем примере.

*n$ идентифицирует аргумент, значение которого должно рассматриваться как формат width - аргумент 1 в вашем примере.

Итак, те, кто писал руководство, следуют стандарту.


Вы утверждаете в комментарии:

2$* должен соответствовать второму параметру, тогда как 1$d должен соответствовать первому, но оказывается, что это неверно в случае printf("%2$*1$d", width, num);.

Как уже отмечалось, стандарт явно прикрепляет части n$ как модификаторы постфикса % и *, а не как модификаторы префикса спецификатора преобразования формата (d в этом примере) и *, Возможно, ваш предполагаемый дизайн можно было бы сработать, но дизайн не был выбран.

Ответ 2

В вашем втором примере:

printf("%2$*1$d", width, num);

Первое число, 2, прикрепляется к спецификатору формата, а второе число 1 прикрепляется к *. Если вы прочитали документацию для printf, это ясно. Ничего необычного не происходит.

Ответ 3

Ширина поля или точность или оба значения могут быть указаны звездочкой '*' или звездочку, за которой следует одна или несколько десятичных цифр и `$ ' вместо цифровой строки.

Итак, 1$ применяется к звездочке, поэтому первым аргументом является ширина. 2$ применяется ко всей спецификации формата, поэтому вторым аргументом является тот, значение которого будет напечатано.

Ответ 4

Я согласен, что справочная страница сбивает с толку, потому что она объясняет два понятия (модификатор длины как позиционный аргумент) в одном примере, поэтому я перехожу к могучей паре vi/gcc:

test.c

#include <stdio.h> 
void main(int argc, char** argv) {
    printf("%1$c\n", 'a', 'b', 'c');
    printf("%2$c\n", 'a', 'b', 'c');
    printf("%3$c\n", 'a', 'b', 'c');
    printf("%3$c %2$c %1$c\n", 'a', 'b', 'c');
}

Компиляция выдаст предупреждения, если используются не все аргументы:

$ gcc test.c
test.c: In function ‘main:
test.c:3:9: warning: unused arguments in $-style format [-Wformat-extra-args]
  printf("%1$d\n", 'a', 'b', 'c');
         ^~~~~~~~
test.c:4:9: warning: format argument 1 unused before used argument 2 in $-style format [-Wformat=]
  printf("%2$d\n", 'a', 'b', 'c');
         ^~~~~~~~
test.c:4:9: warning: unused arguments in $-style format [-Wformat-extra-args]
test.c:5:9: warning: format argument 1 unused before used argument 3 in $-style format [-Wformat=]
  printf("%3$d\n", 'a', 'b', 'c');
         ^~~~~~~~
test.c:5:9: warning: format argument 2 unused before used argument 3 in $-style format [-Wformat=]

Но тут вы видите результат:

$ ./a.out
a
b
c
c b a