在GNU C的printf格式规范中正确使用param-no

dpiehjr4  于 2023-01-12  发布在  其他
关注(0)|答案(1)|浏览(106)

我正在开发一个使用printf格式字符串的helper函数,因此我开始更详细地检查printf格式规范,并发现GNU手册允许使用param-no:www.example.com网站。https://www.gnu.org/software/libc/manual/2.36/html_mono/libc.html#Output-Conversion-Syntax.
我以前从来没有使用过这个特性(因为我觉得它没有用),但是,我希望我的函数能够处理所有可能的格式规范,所以我开始尝试它:

#include <stdio.h>

int main()
{
    double u = 1.23456789;
    double d = 9.87654321;
    
    // Example A                                1.  2. 3.
    printf( "A) Testing param-no: >%3$*.*f<\n", 12, 3, u );
    // Compiler warning: Missing $ operand in format
    // Works.
    
//  // Example B                                1.  2. 3.
//  printf( "B) Testing param-no: >%1$*.*f<\n", 12, 3, u );
//  // Compiler warning: Missing $ operand in format
//  // Prints spaces in an infinite loop.
    
    // Example C                                1.  2. 3.
    printf( "C) Testing param-no: >%3$*.*f<\n", u, 12, 3 );
    // Compiler warning: Missing $ operand in format
    // Works.
    
//  // Example D                                1.  2. 3.
//  printf( "D) Testing param-no: >%1$*.*f<\n", u, 12, 3 );
//  // Compiler warning: Missing $ operand in format
//  // Prints spaces in an infinite loop.
    
    // Example E                                           1.  2. 3. 4. 5. 6.
    printf( "E) Testing param-no: >%3$*.*f<, >%6$*.*f<\n", 12, 3, u, 5, 4, d );
    // Compiler warning: Missing $ operand in format
    // Wrong output: "Testing param-no: >       0.000<, >1.2346<"
    
    // Example F                                           1. 2.  3. 4. 5. 6.
    printf( "F) Testing param-no: >%3$*.*f<, >%6$*.*f<\n", u, 12, 3, d, 5, 4 );
    // Compiler warning: Missing $ operand in format
    // Wrong output: "Testing param-no: >       0.000<, >1.2346<"
    
    // Example G                                    1.  2. 3.
    printf( "G) Testing param-no: >%3$*1$.*2$f<\n", 12, 3, u );
    // Works.
    
    // Example H                                                   1.  2. 3. 4. 5. 6.
    printf( "H) Testing param-no: >%3$*1$.*2$f<, >%6$*4$.*5$f<\n", 12, 3, u, 5, 4, d );
    // Works.
    
    // Example I                                                   1. 2.  3. 4. 5. 6.
    printf( "I) Testing param-no: >%1$*2$.*3$f<, >%4$*5$.*6$f<\n", u, 12, 3, d, 5, 4 );
    // Works.
    
    // Example J                                                   1. 2.  3. 4. 5. 6.
    printf( "J) Testing param-no: >%4$*5$.*6$f<, >%1$*2$.*3$f<\n", u, 12, 3, d, 5, 4 );
    // Works.
    
    // Example K                                                   1. 2.  3. 4. 5. 6.
    printf( "K) Testing param-no: >%1$*3$.*6$f<, >%5$*2$.*4$f<\n", d, 12, 5, 3, u, 4 );
    // Works.
    
    return 0;
}

基于这些结果,我发现相关文件的以下几点具有误导性:

  • 转换规范的第二种通用形式% [ param-no $ ] flags width . * [ param-no $ ] type conversion建议您可以使用星号(*)作为精度,可选param-no后跟美元($),如示例A中所示。但是,这会生成编译器警告,并且要么像示例A中那样工作,要么使程序以无限大的(或者很长,我没有等到它结束)循环,如示例D。
  • 与前一点相关,正如我在阅读宽度和精度部分的描述时所说:* "您还可以指定字段宽度''。这意味着参数列表中的下一个参数(在要打印的实际值之前)用作字段宽度。""您还可以指定精度""。这意味着参数列表中的下一个参数(在要打印的实际值之前)用作精度。"*,我认为宽度和精度值将从适当的位置获取,它是通过获取实际参数的位置(例如,上述任何示例中的u)并将其减少1或2来计算的。但是,这种方法会导致编译器警告和错误输出,如示例E所示。
  • 给出转换规范的两种一般形式表明不存在第三种形式,您可能会得出结论,不允许使用可变参数之一设置宽度,因为在第二种一般形式中,只有精度部分被星号(*)替换。然而,同时设置宽度和精度的工作原理与示例H相同。

我想正确的描述应该是这样的:
printf模板字符串中的所有转换规范都应具有以下形式之一,并且所有转换规范都应具有相同的形式:
% flags [ width-digits | * ] [ . precision-digits | . * ] type conversion

% param-no-a $ flags [ width-digits | * param-no-b $ ] [ . precision-digits | . * param-no-c $ ] type conversion
必需的param-no-a指示实际参数的位置,而可选的param-no-b和param-no-c指示宽度和精度值的位置。
我链接的文档是否正确?
我是否正确理解了param-no的用法?

9o685dep

9o685dep1#

实际上,glibc文档似乎缺乏。使用man 3p fprintfhttps://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html
格式可以包含编号参数转换规范(即“%n$”和“*m$”),也可以包含未编号参数转换规范(即%和 *),但不能同时包含两者。
格式:

printf("%3$*.*f", 12, 3, 1.23);

是无效的。

相关问题