为什么在printf格式字符串中使用%8.8而不是%08?

时间:2017-06-14 22:55:06

标签: c++ c visual-c++ printf format-specifiers

刚刚遇到一些(非常)遗留代码(最初编写为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的遗留方面吗?

编译器详细信息:用于x86的Microsoft(R)C / C ++优化编译器版本19.10.25019

4 个答案:

答案 0 :(得分:16)

对于无符号整数类型,没有区别。对于 signed 整数类型的负值,您将看到不同的输出

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

V1: -0002413
V2: -00002413

.8的{​​{1}}部分指定要显示的最小位数。并且%8.8ld符号不被视为数字。因此,第二个版本需要始终打印8位数。如果值为负,则-符号将无法选择,而是成为第9个字符,因此违反了请求的字段宽度8。

-版本无需打印至少8位数,这就是为什么%08ld符号占据里面的字段宽度为8且只有7位数的原因印刷。

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

答案 1 :(得分: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;
}

输出(macOS Sierra 10.12.5上的GCC 7.1.0):

   %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]

答案 2 :(得分:5)

当您在ref中阅读时,这两个与unsigned long(/整数)一起使用时是等效的。

printf("V1: %08lu \n", val);
当指定填充时,

0标志将使用零(0)而不是空格填充数字(请参阅width子说明符)。

8将是number,在“要打印的最小字符数中。如果要打印的值小于此数字,则结果将填充空格。值为即使结果较大,也不会被截断。“

现在这个:

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

会将8的效果保持为number,但会将.8添加为“{1}}”,对于“整数说明符”(d,i,o,u,x ,X):precision指定要写入的最小位数。如果要写入的值小于此数字,则结果用前导零填充。即使结果较长,该值也不会被截断。 0表示没有为值0写入字符。“。

PS:标准C ++应该发出诊断错误:

.number

然而,即使Stroustrup他自己说这“不是也不是C ++,甚至也不是C”。

答案 3 :(得分:0)

实际上,%8.8和{{1}}之间没有任何区别,除非你进入十进制数字,然后就会有所不同。这一切都取决于用户喜欢它的方式。这只是一种偏好。