Printf conversion specifiers

Hello, this is one examples that I always panic with C printf format specifier.
1) I did read the manpage with

 man 3 printf
...... 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,
           printf("%*d", width, num);
       and
           printf("%2$*1$d", width, num);
       are equivalent.  The second style allows repeated references to the same argument.  

1) Can you please explain this example on width and num for me.
2) To have further understanding I used following code from "A book on C by Al Kelley/Ira Pohl, page 503" to test,

#include <stdio.h>

int main()
{
    int i;
    char c;
    char string[15];
    printf("Please input a line:\n"); 
    
   scanf("%d , %*s %% %c %5s %s", &i, &c, string, &string[5]);
    //input: 45 , ignore_this, % C read_in_this**
    
    printf("%d , %*s %% %c %5s %c", &i, &c, string, &string[5]);
    return 0;
}

but I met two problems. 1) A bunch of warnings as I mentioned in my last threads:

pg503.c: In function �main�:
pg503.c:13:2: warning: format �%d� expects argument of type �int�, but argument 2 has type �int *� [-Wformat]
pg503.c:13:2: warning: field width specifier �*� expects argument of type �int�, but argument 3 has type �char *� [-Wformat]
pg503.c:13:2: warning: format �%c� expects argument of type �int�, but argument 5 has type �char *� [-Wformat]
pg503.c:13:2: warning: format �%s� expects a matching �char *� argument [-Wformat]
pg503.c:13:2: warning: format �%c� expects a matching �int� argument [-Wformat]

2) when I tried:

./a.out 
Please input a line:
45 , ignore_this, % C read_in_this**

Note: Spaces after 45, two asterisks were put at the end on purpose, and I was expecting to see:

45
, 
read_
in_this**

The program went infinite, nothing was printed. Thank you!

You are expecting newline where is \n which is not in your printf statement.

1 Like

You give printf and scanf identical command strings, but %* means something very different to printf and scanf. It means "print this type to [format] characters wide" in printf, and "don't store this value" in scanf.

Also, you are giving printf all the same arguments too. scanf needs &i because scanf needs i's location, not its contents -- but printf needs its contents, not its location!

So, my best guess at what happened is that printf's %d prints part of a memory address (since you are on a 64-bit system, and %d expects a 32-bit integer). Next, %*s tries to get a width, and gets the other half of that memory address -- a very, very high number! So it prints several billion leading spaces before it actually gets to the string.

1 Like

Sorry I put the '\n' in printf(), forgot to change it when I posted it.

printf("%d\n,\n%*s\n%%\n%c\n%5s\n%c", &i, &c, string, &string[5]);
printf("%ld\n,\n%*s\n%%\n%c\n%5s\n%c", &i, &c, string, &string[5]);

However, the same problem still there.
Thank you both!

I'm not sure what you're trying to do with that printf. Putting the same command string in printf as scanf does not make sense here.

1 Like

How stupid to use the scanf format for printf!------Thanks, Corona!
I got it now:

printf("%d\n%%\n%c\n%5s\n%c\n%s\n\n", i, c, string, string[5], string+5);

$ ./a.out
Please input a line:
45 , ignore_this, % C read_in_this**

45
%
C
read_in_this**
i
in_this**

If print string, use the address of the string array(cf: string+5); if print the char of the string, use the subscription (string[5]) explicitly.

1 Like

From our previous discussions, I assume that you understand that the call:

printf("%*d\n", 5, 1);

prints 1 as a right-justified decimal integer in a field 5 characters wide.
This can be restated as printf() "prints the 2nd argument following the format string as a decimal integer in a field whose width is specified by the 1st argument following the format string". Note that the numbers in red match the second call you had to printf:

printf("%2$*%1d\n", 5, 1);

The n$ forms to reference argument positions are most frequently used when dealing with differences in output based on localization. (For example, in the US, dates are often printed in the form MM/DD/YYYY, but in Europe, dates are frequently printed in the form DD-MM-YYYY.) If you have your format strings in a message catalog based on your current locale settings, a printf() call to print a date in the US or Europe might be:

printf(format, m, d, y);

where format would be:

%02d/%02d/%d

to print in US format, and would be:

%2$02d-%1$02d-%3$d

to print in European format. (But, using message catalogs to control which format string is used is another level of complexity that you don't need to worry about for now.)

The statement "The second style allows repeated references to the same argument." may be better explained with a different example. Suppose that you want to print more that one decimal value with a printf statement and you want a variable to control the field width, but you want the field width to be the same for all of the values printed. Try building the following sample program:

#include <stdio.h>
void
ruler(int w) {
    printf("Following output produced with width set to %d\n", w);
    printf("         1111111111222222222233333333334444444444555555555566666\n");
    printf("1234567890123456789012345678901234567890123456789012345678901234\n");
    return;
}

int
main() {
        int     width;  // field width

        for(width = 1; width <= 16; width += width) {
                ruler(width);
                printf("%*d%0*d%*d%*d\n",
                        width, 1, width, 2, width, 3, width, 4);
                printf("%2$*1$d%3$0*1$d%4$*1$d%5$*1$d\n",
                        width, 1, 2, 3, 4);
                printf("%4$*1$d%5$0*1$d%2$*1$d%3$*1$d\n\n",
                        width, 3, 4, 1, 2);
        }
        return;
}

and examine the format strings used in the printf() calls in main() . Do you understand why the width parameter has to be supplied four times in the first call, but only supplied once in the other two calls?

The output the above program produces is:

Following output produced with width set to 1
         1111111111222222222233333333334444444444555555555566666
1234567890123456789012345678901234567890123456789012345678901234
1234
1234
1234

Following output produced with width set to 2
         1111111111222222222233333333334444444444555555555566666
1234567890123456789012345678901234567890123456789012345678901234
 102 3 4
 102 3 4
 102 3 4

Following output produced with width set to 4
         1111111111222222222233333333334444444444555555555566666
1234567890123456789012345678901234567890123456789012345678901234
   10002   3   4
   10002   3   4
   10002   3   4

Following output produced with width set to 8
         1111111111222222222233333333334444444444555555555566666
1234567890123456789012345678901234567890123456789012345678901234
       100000002       3       4
       100000002       3       4
       100000002       3       4

Following output produced with width set to 16
         1111111111222222222233333333334444444444555555555566666
1234567890123456789012345678901234567890123456789012345678901234
               10000000000000002               3               4
               10000000000000002               3               4
               10000000000000002               3               4

Update:
On some systems (including MAC OS/X), flags in the %n$ can come before or after the n$ ; the standards say the flags should follow the n$ . The above examples have been updated to use the standard form.

5 Likes

Thanks Don!
So that 1$ as the first argv, here width; 2$ as 2nd argv as 1, 3rd -> 2, 4th->3 and 5th->4. The order of the argv was switched on purpose for the 3rd printf in main() function.
On my system (Ubuntu 13.10), the second style does not print the leading 0s between 1 and 2, that does not matter to me.
Anyway, How can I, or how long will it take to, get all those tricks?!

You're digging deeper into printf than most people bother to... :slight_smile: You're teaching me new stuff too now :smiley:

Don Cragun :

Your explanation was awesome, and it's crystal clear as always, actually when I was in college even our lecturers didn't teach/explain us any programming language / any single statement in depth as clear as you guys here teaching us.

I have noticed in many threads in forum especially You, Corona, Scrutinizer and RudiC explained so clearly that even beginner who just knows alphabets would be able to create sentence.

Thanks you so much for your valuable time.

As I always say Unix.com is well organized, and got prompt support compare any forum which one can search in any search engine. :b:

Regards,
Akshay

3 Likes

Yes, you have it.

Please, stop calling them tricks! I haven't used any tricks in any of the code I've shown you. (I know some tricks that I could use because I have seen (and written) some of the code that implements various utilities, library routines, and kernel features. But, tricks can disappear or start working differently at any time.) Everything I have used in the examples I've shown you are features documented in the man pages. I know that some implementations take greater care in producing quality man pages than some other implementations, but a good set of man pages (or a copy of the C Standard or the POSIX Standard) is your usually your best reference as long as you can figure out which man page to look for. In the old days, the manuals were available as printed documents and you could read them from cover to cover to get an idea of what was available even if you didn't need it at that time. (Just reading the table of contents gives you more information than you might think.) I reread the (currently 3,906 page) POSIX standard every time it is revised (every 5 to 10 years). There is a bunch of stuff in there I have never needed to use, a bunch of stuff I seldom use, and a bunch of stuff I use every day. After you've read a few dozen man pages, you may get to a point where you notice the symmetry between interfaces and you develop a feel for how things are supposed to work. Ken and Dennis had a great plan when they designed and built their first UNIX System. There has been a lot of unnecessary bloat added to UNIX and Linux systems since then, but the simple, basic principles are still hidden in those manuals.

Enjoy learning. It is a lifelong process!

The stuff you're getting into here isn't stuff that I use regularly. I reread the man pages and played around with code to try to create an example that would help you understand what the words on the man page meant. (And, as you may have noticed, I got something to work on my laptop that didn't use the format specified by the standards. That may have been why it didn't work on Ubuntu. In case you didn't notice, my original posting had the 0 fill flag in the wrong place. My original example had:

                printf("%2$*1$d%03$*1$d%4$*1$d%5$*1$d\n",
                        width, 1, 2, 3, 4);
                printf("%4$*1$d%05$*1$d%2$*1$d%3$*1$d\n\n",
                        width, 3, 4, 1, 2);

but has been fixed in message #7 in this thread to follow the form specified by the standards:

                printf("%2$*1$d%3$0*1$d%4$*1$d%5$*1$d\n",
                        width, 1, 2, 3, 4);
                printf("%4$*1$d%5$0*1$d%2$*1$d%3$*1$d\n\n",
                        width, 3, 4, 1, 2);

Both happen to work on OS X, but the standards specify that flags are supposed to follow the n$ that specifies which argument is to be processed and before the optional minimum field width, precision, and length modifiers, and the mandatory conversion specifier character. If you used the original form and it didn't work on Ubuntu, you may want to try using the corrected format strings.)

1 Like