Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a "null" printf code that doesn't print anything, used to skip a parameter?

If I want a program to have multiple text output formats, I could do something like this:

const char *fmtDefault = "%u x %s ($%.2f each)\n";
const char *fmtMultiLine = "Qty: %3u\nItem: %s\nPrice per item: $%.2f\n\n";
const char *fmtCSV = "%u,%s,%.2f\n";

const char *fmt;
switch (which_format) {
    case 1: fmt = fmtMultiLine; break;
    case 2: fmt = fmtCSV; break;
    default: fmt = fmtDefault;
}

printf(fmt, quantity, item_description, price);

Since the price is specified last, I could also add one that doesn't list prices:

const char *fmtNoPrices = "%u x %s\n";

But what if I want to omit the quantity instead? If I did this:

const char *fmtNoQuantity = "The price of %s is $%.2f each.\n";

then undefined behavior (most likely a segfault) will occur rather than what I want. This is because it will treat the first parameter as a pointer to a string, even though it's actually an unsigned int. This unsigned int will most likely point to something other than valid string data, or (much more likely, especially if you're not buying hundreds of millions of the same item) an invalid memory location, resulting in a segmentation fault.

What I want to know is if there's a code I can put somewhere (%Z in this example) to tell it to skip that parameter, like this:

const char *fmtNoQuantity = "%ZThe price of %s is $%.2f each.";
like image 412
flarn2006 Avatar asked May 23 '13 06:05

flarn2006


2 Answers

For %s values, there is a “null” printf() code: %.0s.

You could reach a general solution via:

When possible, re-arrange so that non-%s values are last, and then under specify the format string.

My favorite for you is to have 3 separate printf() calls, one for each value using its own format. When the value is not needed, simply supply a format string with no specifiers.

const char * Format1q   = "";
const char * Format1id  = "The price of %s";
const char * Format1p   = " is $%.2f each.\n";
...
printf(Format1q,  quantity); 
printf(Format1id, item_description);
printf(Format1p,  price);

Weird solutions:

For other values that are the same size you could attempt the Undefined Behavior of also using %.0s. (worked with some samples in gcc 4.5.3, who knows in other compilers or the future.)

For other values that are the N x the same size as a pointer size you could attempt the Undefined Behavior of also using %.0s N times. (worked with some samples in gcc 4.5.3, who knows in other compilers or the future.)

like image 99
chux - Reinstate Monica Avatar answered Nov 03 '22 08:11

chux - Reinstate Monica


I actually figured this out on my own while looking something up for my question. You can prepend a parameter number, followed by a $ to the format code, after the %. So it would be like this:

const char *fmtNoQuantity = "The price of %2$s is $%3$.2f each.";

That is, the string would use the 2nd parameter, and the float would use the 3rd parameter. Note, however, that this is a POSIX extension, not a standard feature of C.

A better method would probably be to define a custom printing function. Something like this:


typedef enum {fmtDefault, fmtMultiLine, fmtCSV, fmtNoPrices, fmtNoQuantity} fmt_id;

void print_record(fmt_id fmt, unsigned int qty, const char *item, float price)
{
    switch (fmt) {
    case fmtMultiLine:
        printf("Qty: %3u\n", qty);
        printf("Item: %s\n", item);
        printf("Price per item: $%.2f\n\n", price);
        break;
    case fmtCSV:
        printf("%u,%s,%.2f\n", qty, item, price);
        break;
    case fmtNoPrices:
        printf("%u x %s\n", qty, item);
        break;
    case fmtNoQuantity:
        printf("The price of %s is $%.2f each.\n", item, price);
        break;
    default:
        printf("%u x %s ($%.2f each)\n", qty, item, price);
        break;
    }
}
like image 37
BrutalCaeser Avatar answered Nov 03 '22 09:11

BrutalCaeser