我正在开发一个使用printf格式字符串的辅助函数,因此我开始更详细地检查printf格式规范,并发现GNU的手册允许使用param-no: 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的用法正确吗?
确实,glibc文档似乎是缺乏的。使用man 3p fprintf
https://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html:
格式可以包含带编号的参数转换规范(即"%n$"one_answers"*m$"),或无编号的参数转换规范(即%和*),但不能同时使用。
的格式:
printf("%3$*.*f", 12, 3, 1.23);
是无效的