How do positional arguments like "1$" work with printf()?
Asked Answered
B

4

21

By man I find

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

and

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

are equivalent.

But IMO the second style should be the same as:

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

However via testing it seems man is right; why?

Burma answered 12/6, 2011 at 14:41 Comment(1)
2$* should match the 2nd parameter while 1$d should match the 1st one,but it turns out that it's not true in the case of printf("%2$*1$d", width, num);Burma
D
32

The relevant part of the POSIX specification of printf() defines this behaviour:

Conversions can be applied to the nth argument after the format in the argument list, rather than to the next unused argument. In this case, the conversion specifier character % (see below) is replaced by the sequence "%n$", where n is a decimal integer in the range [1,{NL_ARGMAX}], giving the position of the argument in the argument list. This feature provides for the definition of format strings that select arguments in an order appropriate to specific languages (see the EXAMPLES section).

The format can contain either numbered argument conversion specifications (that is, "%n$" and "*m$"), or unnumbered argument conversion specifications (that is, % and * ), but not both. The only exception to this is that %% can be mixed with the "%n$" form. The results of mixing numbered and unnumbered argument specifications in a format string are undefined. When numbered argument specifications are used, specifying the Nth argument requires that all the leading arguments, from the first to the (N-1)th, are specified in the format string.

In format strings containing the "%n$" form of conversion specification, numbered arguments in the argument list can be referenced from the format string as many times as required.

The %n$ identifies the argument whose value is to be printed - argument 2 in your example.

The *n$ identifies the argument whose value is to be treated as the format width - argument 1 in your example.

So, those writing the manual followed the standard.


You argue in a comment:

2$* should match the 2nd parameter while 1$d should match the first one, but it turns out that it's not true in the case of printf("%2$*1$d", width, num);.

As noted already, the standard clearly attaches the n$ parts as postfix modifiers of % and *, rather than as prefix modifiers of the format conversion specifier (d in this example) and *. Your putative design could, probably, be made to work, but was not the design chosen.

Digamy answered 12/6, 2011 at 14:55 Comment(0)
P
5

In your second example:

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

The first number, 2, is attached to the format specifier, and the second number, 1, is attached to the *. If you read the documentation for printf, this is clear. Nothing unusual is happening.

Pegeen answered 12/6, 2011 at 14:55 Comment(0)
U
1

A field width or precision, or both, may be indicated by an asterisk '*' or an asterisk followed by one or more decimal digits and a `$' instead of a digit string.

So the 1$ applies to the asterisk, therefore the first argument is the width. The 2$ applies to the entire format specification, therefore the second argument is the one whose value will be printed.

Ultrafilter answered 12/6, 2011 at 14:56 Comment(0)
P
1

I agree that the man page is confusing because it explains two concepts (length modifier as positional argument) in one example, so I go to the mighty couple 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');
}

Compiling will give warnings if not all arguments are used:

$ 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=]

But then here you see the result:

$ ./a.out
a
b
c
c b a
Pow answered 10/1, 2019 at 17:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.