Зачем использовать%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 цифр.
Как я отмечал в комментарии, между строками есть разница между %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
если вы не попали в десятичные числа, то есть разница. Все зависит от того, как это нравится пользователю. Это просто предпочтение.