关于printf
函数,我从一些参考文献和实验中理解了以下内容。
- 当我们尝试使用用于浮点(或)双精度的格式说明符打印整数值时,反之亦然,则行为是不可预测的
- 但是可以使用
%c
来打印与整数值相等的字符。使用%d
打印字符的ASCII值(整数表示)也是可以接受的
类似地,如果格式说明符和传递给scanf的自变量不匹配,scanf
的行为是什么。标准定义了它吗?
变量参数(与省略号...
匹配的参数)是默认提升的。这意味着所有较短的积分类型都被提升为int
(或无符号,视情况而定)。integers和字符(我相信)。printf
中%d
和%c
之间的区别仅在于值的格式。
scanf
是另一回事。您传递的所有参数都是指针。没有默认的pro莫&害羞;指针之间的关系,并且传递与指针对象类型匹配的精确格式说明符是至关重要的。
在任何一种情况下,如果您的格式说明符与提供的参数不匹配(例如,在printf
中将int *
传递给%p
),结果都是未定义的行为,这远比"不可预测"糟糕——这意味着您的程序只是格式错误。
printf
和scanf
令人困惑,因为它们是非常特殊和不寻常的函数。它们都接受可变数量的参数。这意味着匹配参数类型的规则以及可能发生的自动转换与其他函数不同。
对于大多数函数,编译器确切地知道需要什么类型的参数。例如,如果您调用
#include <math.h>
int i = 144;
printf("%fn", sqrt(i));
它运行良好。sqrt
函数需要一个类型为double
的参数,但编译器知道这一点,所以它可以插入一个自动转换,就像编写一样
printf("%fn", sqrt((double)i));
(脚注:编译器从<math.h>
中的函数原型中知道sqrt
的预期参数类型。)
但是printf
接受数量可变的任何类型的参数。只要格式说明符匹配,您几乎可以传递任何内容:
int i1 = 12, i2 = 34;
float f1 = 56.78, f2 = 9.10;
printf("%d %d %f %fn", i1, i2, f1, f2);
printf("%f %f %d %dn", f1, f2, i1, i2);
但这是关键:只要格式说明符匹配。在对参数数量可变的函数(如printf
)的调用中,编译器不会尝试(甚至不允许尝试)将每个参数转换为相应格式说明符所需的类型。因此,如果存在严重的不匹配,比如试图使用%f
打印整数,这是行不通的,事实上可能会发生疯狂的事情。
但是,为了让事情变得有趣,当您调用一个具有可变数量参数的函数(如printf
)时,会自动执行另一组转换。它们被称为默认参数提升。基本上,char
或short
类型的任何内容都自动转换("升级")为int
,而float
类型的任何东西都自动转换为double
。因此,您可以处理一些不匹配:您可以使用%d
打印char
或short
,也可以使用%f
打印float
或double
。由于char
总是升级为int
,这意味着%c
实际上将接收一个int
,这意味着您可以传递一个常规的int
,并使用%c
打印它。
但这很容易出错,尤其是当我们调用其他函数(如sqrt
)时,我们习惯于编译器为我们正确地转换所有内容。因此,一个好的编译器会查看格式字符串,并使用它来预测应该传递哪些类型的参数,如果传递了错误的内容,编译器可能会发出警告。如果你的编译器没有发出这样的警告,那么最好弄清楚如何启用它们,如果没有,也许可以得到一个更好的编译器。
然后我们来到scanf
。scanf
还接受数量可变的参数,这些参数应该与格式字符串上的说明符相匹配。然而,对于scanf
,您传递的所有参数都是指针,指向您要求scanf
用它读取的值填充的变量。事实证明,这意味着没有自动转换是可能的,并且必须将所有参数作为正确类型的指针传递。下面的表格列出了其中的一些:
说明符 | 相应参数 |
---|---|
%c | 指向char |
%hd | 指向short 的指针 |
%d | 指向int |
%ld | 指向long |
%f | 指向float |
%lf | 指向double |
%s | 指向char 的指针[注意] |