简体   繁体   English

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

[英]Correct use of param-no in GNU C's printf format specification

I am working on a helper function using printf format strings, so I started to examine printf format specifications in more detail, and found that GNU's manual allows the use of a param-no: https://www.gnu.org/software/libc/manual/2.36/html_mono/libc.html#Output-Conversion-Syntax .我正在使用 printf 格式字符串开发一个助手 function,所以我开始更详细地检查 printf 格式规范,发现 GNU 手册允许使用参数号: https://www.gnu.org/software/ libc/manual/2.36/html_mono/libc.html#Output-Conversion-Syntax

I have never used this feature before (as I did not find it useful), however, I would like my function to process all possible format specifications, so I started experimenting with it:我以前从未使用过这个功能(因为我没有发现它有用),但是,我希望我的 function 能够处理所有可能的格式规范,所以我开始试验它:

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

Based on these results, I find the following points of the related documentation misleading:基于这些结果,我发现相关文档的以下几点具有误导性:

  • The second general form of the conversion specification, % [ param-no $ ] flags width. * [ param-no $ ] type conversion转换规范的第二种一般形式, % [ param-no $ ] flags width. * [ param-no $ ] type conversion % [ param-no $ ] flags width. * [ param-no $ ] type conversion suggests that you can give an asterisk ( * ) as the precision and an optional param-no followed by a dollar ( $ ), like in Example A. However, this generates a compiler warning, and either works as in Example A, or makes the program print spaces in an infinite (or very long, I did not wait until the end of it) loop as in Example D. % [ param-no $ ] flags width. * [ param-no $ ] type conversion建议您可以给出一个星号 ( * ) 作为精度和一个可选的 param-no 后跟一个美元 ( $ ),就像示例 A 中那样。但是,这会生成编译器警告,并且要么像示例 A 那样工作,要么像示例 D 那样使程序在无限(或非常长,我没有等到它结束)循环中打印空格。

  • Related to the previous point, as I was reading the description of the width and precision parts: "You can also specify a field width of '*'. This means that the next argument in the argument list (before the actual value to be printed) is used as the field width."与前一点相关,当我阅读宽度和精度部分的描述时: “您还可以指定字段宽度为‘*’。这意味着参数列表中的下一个参数(在要打印的实际值之前) ) 用作字段宽度。” and "You can also specify a precision of '*'. This means that the next argument in the argument list (before the actual value to be printed) is used as the precision."“您还可以指定精度为‘*’。这意味着参数列表中的下一个参数(在要打印的实际值之前)用作精度。” , I thought that the width and precision values will be fetched from the appropriate position, which is calculated by taking the position of the actual paramater (eg u in any of the above examples), and reducing it by 1 or 2. However, this approach leads to a compiler warning and wrong output as in Example E. ,我认为宽度和精度值将从适当的 position 中获取,这是通过获取实际参数的 position(例如上述任何示例中的u )并将其减少 1 或 2 来计算的。但是,这方法会导致编译器警告和错误 output,如示例 E 所示。

  • Presenting the two general forms of the conversion specification suggests that there is no third form, and you may conclude that setting the width with one of the variadic arguments is not allowed because only the precision part is replaced by an asterisk ( * ) in the second general form.呈现转换规范的两个通用 forms 表明没有第三种形式,您可能会得出结论,不允许使用可变参数 arguments 之一设置宽度,因为在第二个中只有精度部分被星号 ( * ) 替换一般形式。 However, setting both the width and the precision at the same time works as in eg Example H.但是,同时设置宽度和精度会像示例 H 中那样工作。

I suspect the correct description would be the following:我怀疑正确的描述如下:

All conversion specifications in a printf template string should have one of the following forms, and all of them should have the same form: printf 模板字符串中的所有转换规范都应具有以下 forms 之一,并且它们都应具有相同的形式:

% flags [ width-digits | * ] [. precision-digits |. * ] type conversion

or或者

% param-no-a $ flags [ width-digits | * param-no-b $ ] [. precision-digits |. * param-no-c $ ] type conversion

The mandatory param-no-a indicates the position of the actual parameter, while the optional param-no-b and param-no-c indicate the position of the width and precision values.必选的param-no-a表示实参的position,可选的param-no-b和param-no-c表示宽精度值的position。

Is the documentation I linked the correct one?我链接的文档是否正确?

Do I understand the use of param-no correctly?我是否正确理解 param-no 的用法?

Indeed, the glibc documentation seems to be lacking.事实上,似乎缺少 glibc 文档。 Use man 3p fprintf https://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html :使用man 3p fprintf https://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html

The format can contain either numbered argument conversion specifications (that is, "%n$" and "*m$"), or unnumbered argument conversion specifications (that is, % and * ), but not both.该格式可以包含编号参数转换规范(即“%n$”和“*m$”)或未编号参数转换规范(即 % 和 * ),但不能同时包含两者。

The format:格式:

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

is just invalid.只是无效的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM