6

Just came across some (very) legacy code (originally written as C but now compiled as C++ too) and a use of the width specifier to zero pad a string

void main()
{
    unsigned long val = 2413;
    printf("V1: %08lu \n", val);
    printf("V2: %8.8lu \n", val);
}

The results are identical

V1: 00002413
V2: 00002413

So why would one use V2? Was it some legacy aspect of the std lib from the days of yore?

Compiler details : Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25019 for x86

Yakk - Adam Nevraumont
  • 235,777
  • 25
  • 285
  • 465
Preet Sangha
  • 61,126
  • 17
  • 134
  • 202
  • 1
    With strings, there's a difference between `%8s` and `%8.8s` — the latter truncates if the string is longer than 8. There's another difference; `0` is not a valid modifier for `%s`. The `%8.8lu` doesn't seem to buy you much compared with `%.8lu`; there's not much difference between `%.8lu` and `%08lu`, though `0` is older and the `.` was added to C90 (so that's pretty old these days). – Jonathan Leffler Jun 14 '17 at 22:57
  • They're just two different ways of achieving the same thing. The first tends to be preferred because the padding character is stated explicitly. – Barmar Jun 14 '17 at 23:00
  • `printf` is a function of the C standard library. The question is not about C++. – too honest for this site Jun 14 '17 at 23:00
  • 4
    Your code can't be standard C++; C++ requires that the return value of `main()` is an `int`. C allows implementations to let you use alternative return types; the Microsoft C implementation allows you to use `void`. Their C++ may also allow it, but it is in contravention of the standard which does not. See [What should `main()` return in C and C++](http://stackoverflow.com/questions/204476/) for the details. – Jonathan Leffler Jun 14 '17 at 23:06
  • If you want to keep twice language tags, for me your question is too broad, you need to understand that `printf()` has different standard in C and C++ in 2017. Like `printf()` is idiomatic C, I will advise you to choice C. You could create two question if you want C++ too. – Stargateur Jun 16 '17 at 17:51
  • 4
    Note that this question is the subject of a MSO question — [User goes into edit war for C tag removal](https://meta.stackoverflow.com/questions/350785/user-goes-into-edit-war-for-c-tag-removal/). Please be cautious about editing the question or its tags. – Jonathan Leffler Jun 16 '17 at 18:43

4 Answers4

16

For unsigned integer types there's no difference. For negative values of signed integer types you will see different output

long val = -2413;
printf("V1: %08ld\n", val);
printf("V2: %8.8ld\n", val);

V1: -0002413
V2: -00002413

The .8 part of %8.8ld specifies the minimum number of digits to appear. And the - sign is not considered a digit. For this reason, the second version is required to always print 8 digits. If the value is negative, the - sign will have no choice but to become the 9th character printed, thus violating the requested field width of 8.

The %08ld version has no requirement to print at least 8 digits, which is why the - sign occupies one character inside the field width of 8 and only 7 digits are printed.

http://coliru.stacked-crooked.com/a/fc9022cc0ef3e097

AnT
  • 291,388
  • 39
  • 487
  • 734
  • Hmm are you sure? – gsamaras Jun 14 '17 at 23:16
  • 2
    @gsamaras: Yes. `.8` part specifies *the minimum number of digits to appear*. Which means that you will always see 8 digits. The `-` does not count. This allows it to break out of 8 character field witdth. In the first version there's no "minimum" requirement, which is why `-` eats aways one space and results in less digits being output. – AnT Jun 14 '17 at 23:19
  • AnT great, just checked and you right. Yes I know about that ( see my answer =) ), I was just wasn't sure. +1. – gsamaras Jun 14 '17 at 23:21
  • "For unsigned integer types there's no difference." --> `"#"` flag will impart a difference with unsigned values. `"%#08lx"` vs `"%#8.8lx"` – chux - Reinstate Monica Dec 31 '17 at 13:55
7

As I noted in a comment, with strings, there's a difference between %8s and %8.8s — the latter truncates if the string is longer than 8. There's another difference; 0 is not a valid modifier for %s. The %8.8lu isn't really different from %.8lu; there's not much difference between %.8lu and %08lu, though 0 is older and the . was added to C90 (so that's pretty old these days). There's a difference between %.8ld and %08ld, though, when the values are negative.

Here's some code that illustrates some of the vagaries of the integer formats for printf() — for both signed and unsigned values. Note that if you have %8.6lu rather than %8.8lu (similarly for signed), you get interesting differences.

#include <stdio.h>

static void test_ul(void)
{
    char *fmt[] =
    {
        "%08lu",
        "%8.8lu",
        "%.8lu",
        "%8.6lu",
        "%6.8lu",
    };
    enum { NUM_FMT = sizeof(fmt) / sizeof(fmt[0]) };
    unsigned long val[] = { 2413LU, 234512349LU };
    enum { NUM_VAL = sizeof(val) / sizeof(val[0]) };
    for (int i = 0; i < NUM_FMT; i++)
    {
        for (int j = 0; j < NUM_VAL; j++)
        {
            printf("%8s: [", fmt[i]);
            printf(fmt[i], val[j]);
            puts("]");
        }
    }
}

static void test_sl(void)
{
    char *fmt[] =
    {
        "%08ld",
        "%8.8ld",
        "%.8ld",
        "%8.6ld",
        "%6.8ld",
    };
    enum { NUM_FMT = sizeof(fmt) / sizeof(fmt[0]) };
    long val[] = { +2413L, -2413L, +234512349L, -234512349L };
    enum { NUM_VAL = sizeof(val) / sizeof(val[0]) };
    for (int i = 0; i < NUM_FMT; i++)
    {
        for (int j = 0; j < NUM_VAL; j++)
        {
            printf("%8s: [", fmt[i]);
            printf(fmt[i], val[j]);
            puts("]");
        }
    }
}

int main(void)
{
    test_ul();
    test_sl();
    return 0;
}

Output (GCC 7.1.0 on macOS Sierra 10.12.5):

   %08lu: [00002413]
   %08lu: [234512349]
  %8.8lu: [00002413]
  %8.8lu: [234512349]
   %.8lu: [00002413]
   %.8lu: [234512349]
  %8.6lu: [  002413]
  %8.6lu: [234512349]
  %6.8lu: [00002413]
  %6.8lu: [234512349]
   %08ld: [00002413]
   %08ld: [-0002413]
   %08ld: [234512349]
   %08ld: [-234512349]
  %8.8ld: [00002413]
  %8.8ld: [-00002413]
  %8.8ld: [234512349]
  %8.8ld: [-234512349]
   %.8ld: [00002413]
   %.8ld: [-00002413]
   %.8ld: [234512349]
   %.8ld: [-234512349]
  %8.6ld: [  002413]
  %8.6ld: [ -002413]
  %8.6ld: [234512349]
  %8.6ld: [-234512349]
  %6.8ld: [00002413]
  %6.8ld: [-00002413]
  %6.8ld: [234512349]
  %6.8ld: [-234512349]
Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
5

These two are equivalent when used with an unsigned long (/integers), as you read in the ref.

printf("V1: %08lu \n", val);

0 flag will left-pads the number with zeroes (0) instead of spaces when padding is specified (see width sub-specifier).

8 will be the number, in "Minimum number of characters to be printed. If the value to be printed is shorter than this number, the result is padded with blank spaces. The value is not truncated even if the result is larger."


Now this:

printf("V2: %8.8lu \n", val);

will keep the effects of 8 as number, but will add .8, as .number in "For integer specifiers (d, i, o, u, x, X): precision specifies the minimum number of digits to be written. If the value to be written is shorter than this number, the result is padded with leading zeros. The value is not truncated even if the result is longer. A precision of 0 means that no character is written for the value 0.".


PS: Standard C++ should issue a diagnostic error as such:

prog.cc:3:11: error: '::main' must return 'int'
 void main()
           ^

however, even Stroustrup himself says that this "is not and never has been C++, nor has it even been C".

gsamaras
  • 66,800
  • 33
  • 152
  • 256
  • 1
    This code can definitely compile C and arguably as C++ as well. In C the standard gives explicit permission for `main` to be defined "[...]in some other implementation-defined manner" (§5.1.2.2.1/1). In C++, the compiler is required to "issue at least one diagnostic message" (§[intro.compliance/2]), but having done so, is free to continue to compile the program. "[...]this International Standard states only requirements on C++ implementations, those requirements are often easier to understand if they are phrased as requirements on programs, parts of programs, or execution of programs."(ibid) – Jerry Coffin Jun 16 '17 at 01:42
  • In addition, the code could in theory be for a freestanding systems, where all forms of main() are allowed (though the presence of VS gives away that this is a hosted Windows system). – Lundin Jun 16 '17 at 09:51
  • @JerryCoffin didn't know that, updated my answer! =) – gsamaras Jun 16 '17 at 14:28
0

Actually, it doesn't make any difference between %08 and %8.8 unless you get into decimal numbers, then there is a difference. It all depends on how the user likes it. It is just a preference.

Manav Dubey
  • 738
  • 11
  • 26