Зачем использовать%8.8 вместо%08 в строке формата printf?

Просто наткнулся на некоторый (очень) унаследованный код (изначально написанный как C, но теперь тоже скомпилированный как C++) и использование спецификатора ширины для заполнения нуля строки

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

Результаты идентичны

V1: 00002413
V2: 00002413

Так зачем использовать V2? Был ли это какой-то унаследованный аспект std lib со времен прошлого?

Сведения о компиляторе: Microsoft (R) C/C++ Оптимизирующая версия компилятора 19.10.25019 для x86

4 ответа

Для целочисленных типов без знака нет никакой разницы. Для отрицательных значений целочисленных типов со знаком вы увидите различный вывод

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

V1: -0002413
V2: -00002413

.8 часть %8.8ld указывает минимальное количество отображаемых цифр. И - знак не считается цифрой. По этой причине вторая версия обязана всегда печатать 8 цифр. Если значение отрицательное, - У знака не будет иного выбора, кроме как стать напечатанным 9-м символом, нарушив тем самым требуемую ширину поля 8.

%08ld версия не требует печати по крайней мере 8 цифр, поэтому - знак занимает один символ внутри поля шириной 8 и печатается только 7 цифр.

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

Как я отмечал в комментарии, между строками есть разница между %8s а также %8.8s - последний усекается, если строка длиннее 8, Есть еще одно отличие; 0 не является допустимым модификатором для %s, %8.8lu на самом деле не отличается от %.8lu; нет большой разницы между %.8lu а также %08lu, хоть 0 старше и . был добавлен в C90 (так что это довольно старый в наши дни). Есть разница между %.8ld а также %08ld Впрочем, когда значения отрицательные.

Вот некоторый код, который иллюстрирует некоторые капризы целочисленных форматов для printf() - как для подписанных, так и для беззнаковых значений. Обратите внимание, что если у вас есть %8.6lu скорее, чем %8.8lu (аналогично для подписанных), вы получите интересные отличия.

#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;
}

Вывод (GCC 7.1.0 на 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]

Эти два эквивалентны при использовании с unsigned long (/ целые числа), как вы читаете в ссылке.

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

0 флаг будет дополнять цифры нулями (0) вместо пробелов, если задано заполнение (см. под-спецификатор ширины).

8 будет number, в "Минимальное количество символов для печати. ​​Если значение для печати короче этого числа, результат заполняется пробелами. Значение не усекается, даже если результат больше".


Теперь это:

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

сохранит эффект 8 как number, но добавлю .8, как .number в "Для целочисленных спецификаторов (d, i, o, u, x, X): точность задает минимальное количество записываемых цифр. Если записываемое значение короче этого числа, результат дополняется начальными нулями. Значение не усекается, даже если результат длиннее. Точность 0 означает, что для значения 0 не записано ни одного символа. ".


PS: Стандарт C++ должен выдавать диагностическую ошибку как таковую:

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

однако даже сам Страуструп говорит, что это "не было и никогда не было C++, и даже не было C".

На самом деле, это не имеет никакого значения между %08 а также %8.8 если вы не попали в десятичные числа, то есть разница. Все зависит от того, как это нравится пользователю. Это просто предпочтение.

Другие вопросы по тегам