关于printf函数,我从几篇参考文献和实验中了解到以下几点。
printf
%c
%d
类似地,如果格式说明符和传递给scanf的参数不匹配,scanf的行为是什么?标准定义了吗?
scanf
yrwegjxp1#
变量参数(与省略号...相匹配的变量)是 * default-promitted *,这意味着所有较短的整型类型都被提升为int(或无符号类型)。整型和字符(I believe)之间没有区别。%d和%c在printf中的区别仅仅是值的 * 格式 *。scanf则完全不同,你传递的所有参数都是 pointers,指针之间没有default-pro motion,而且你传递的格式说明符必须与指针对象的类型完全匹配。在这两种情况下,如果您的格式说明符与提供的参数不匹配(例如,将int *传递给printf中的%p),则结果是 *undefined behavior *,这比“不可预测”要糟糕得多--这意味着您的程序只是格式错误。
...
int
int *
%p
56lgkhnf2#
printf和scanf很容易让人混淆,因为它们是非常特殊的函数。它们都接受 * 可变数量的参数 *。这意味着匹配参数类型的规则,以及可能发生的自动转换,与其他函数不同。对于大多数函数,编译器确切地知道需要什么类型的参数。例如,如果调用
#include <math.h> int i = 144; printf("%f\n", sqrt(i));
sqrt函数需要一个double类型的参数,但编译器知道这一点,所以它可以插入一个自动转换,就像您编写
sqrt
double
printf("%f\n", sqrt((double)i));
(脚注:编译器从<math.h>中找到的函数原型中知道sqrt的预期参数类型。)但是printf接受任意类型的参数,只要格式说明符匹配,你可以传递任何参数:
<math.h>
int i1 = 12, i2 = 34; float f1 = 56.78, f2 = 9.10; printf("%d %d %f %f\n", i1, i2, f1, f2); printf("%f %f %d %d\n", f1, f2, i1, i2);
但这是关键:* 只要格式说明符与 * 匹配。在调用参数数目可变的函数(如printf)时,编译器不会尝试(甚至不允许尝试)将每个参数转换为相应格式说明符所期望的类型。因此,如果存在严重的不匹配,比如试图使用%f打印整数,它将不起作用,事实上疯狂的事情也会发生。但是,为了让事情更有趣,当你调用一个参数个数可变的函数时,比如printf,会自动执行另一组转换。它们被称为 default argument promotions。基本上,char或short类型的任何东西都会被自动转换(“提升”)为int,并且float类型的任何内容都会自动转换为double。您可以使用%d打印char或short,也可以使用%f打印float或double。由于char总是提升为int,这意味着%c实际上将接收int,这意味着您可以传递一个常规的int,并使用%c打印它。但是很容易出错,特别是当我们调用其他函数,比如sqrt时,我们已经习惯了编译器为我们正确地转换所有的东西。所以,一个好的编译器会偷看格式字符串,并用它来预测你应该传递什么类型的参数,如果你传递了错误的东西,编译器会发出警告。如果你的编译器没有发出这样的警告,最好弄清楚如何启用它们,如果没有的话,也许可以使用一个更好的编译器。scanf也接受可变数量的参数,这些参数应该与格式字符串上的说明符相匹配。但是,对于scanf,* 所有 * 传递的参数都是指针。你要求scanf用它读到的值来填充的变量。这意味着 * 没有 * 自动转换是可能的,并且必须将所有参数作为正确类型的指针进行传递。下表列出了其中的一些类型:| 说明符|相应的变元|| - -|- -|| x1米32英寸|指向char的指针|| x1米34英寸|指向short的指针|| x1米36英寸1x|指向int的指针|| x1米38英寸|指向long的指针|| x1米40英寸|指向float的指针|| %lf|指向double的指针|| %s个|指向char的指针[注]|所以没有从char和short到int的自动转换,就像printf一样,也没有从float到double的自动转换,如果你阅读的是float,你必须使用%f,如果你读的是double,你必须使用%d。不过,一个好的编译器会再次警告您任何不匹配的情况,找出并注意这些警告是一个非常好的主意!当你调用scanf时,有一个“转换”会自动发生,但它与scanf无关。如果你使用%s读取一个字符串,你应该传递一个pointer-to-char类型的参数,尽管它必须是一个指向一些连续字符的指针。因为scanf可能不会读取一个字符它可能会读取一个字符串,所以很常见的做法是给scanf传递一个字符数组,这没关系,因为在C语言的表达式中使用数组时,比如给函数传递数组(如scanf),使用的值是指向数组第一个元素的指针,它工作正常,并且是scanf所期望的。
%f
char
short
float
long
%lf
%s
char *p = malloc(10); scanf("%9s", p);
和
char a[1-]; scanf("%9s", a);
(请注意,在这种情况下,您 * 不 * 需要在传递给scanf的变量上使用&。)
&
2条答案
按热度按时间yrwegjxp1#
变量参数(与省略号
...
相匹配的变量)是 * default-promitted *,这意味着所有较短的整型类型都被提升为int
(或无符号类型)。整型和字符(I believe)之间没有区别。%d
和%c
在printf
中的区别仅仅是值的 * 格式 *。scanf
则完全不同,你传递的所有参数都是 pointers,指针之间没有default-pro motion,而且你传递的格式说明符必须与指针对象的类型完全匹配。在这两种情况下,如果您的格式说明符与提供的参数不匹配(例如,将
int *
传递给printf
中的%p
),则结果是 *undefined behavior *,这比“不可预测”要糟糕得多--这意味着您的程序只是格式错误。56lgkhnf2#
printf
和scanf
很容易让人混淆,因为它们是非常特殊的函数。它们都接受 * 可变数量的参数 *。这意味着匹配参数类型的规则,以及可能发生的自动转换,与其他函数不同。对于大多数函数,编译器确切地知道需要什么类型的参数。例如,如果调用
sqrt
函数需要一个double
类型的参数,但编译器知道这一点,所以它可以插入一个自动转换,就像您编写(脚注:编译器从
<math.h>
中找到的函数原型中知道sqrt
的预期参数类型。)但是
printf
接受任意类型的参数,只要格式说明符匹配,你可以传递任何参数:但这是关键:* 只要格式说明符与 * 匹配。在调用参数数目可变的函数(如
printf
)时,编译器不会尝试(甚至不允许尝试)将每个参数转换为相应格式说明符所期望的类型。因此,如果存在严重的不匹配,比如试图使用%f
打印整数,它将不起作用,事实上疯狂的事情也会发生。但是,为了让事情更有趣,当你调用一个参数个数可变的函数时,比如
printf
,会自动执行另一组转换。它们被称为 default argument promotions。基本上,char
或short
类型的任何东西都会被自动转换(“提升”)为int
,并且float
类型的任何内容都会自动转换为double
。您可以使用%d
打印char
或short
,也可以使用%f
打印float
或double
。由于char
总是提升为int
,这意味着%c
实际上将接收int
,这意味着您可以传递一个常规的int
,并使用%c
打印它。但是很容易出错,特别是当我们调用其他函数,比如
sqrt
时,我们已经习惯了编译器为我们正确地转换所有的东西。所以,一个好的编译器会偷看格式字符串,并用它来预测你应该传递什么类型的参数,如果你传递了错误的东西,编译器会发出警告。如果你的编译器没有发出这样的警告,最好弄清楚如何启用它们,如果没有的话,也许可以使用一个更好的编译器。scanf
也接受可变数量的参数,这些参数应该与格式字符串上的说明符相匹配。但是,对于scanf
,* 所有 * 传递的参数都是指针。你要求scanf
用它读到的值来填充的变量。这意味着 * 没有 * 自动转换是可能的,并且必须将所有参数作为正确类型的指针进行传递。下表列出了其中的一些类型:| 说明符|相应的变元|
| - -|- -|
| x1米32英寸|指向
char
的指针|| x1米34英寸|指向
short
的指针|| x1米36英寸1x|指向
int
的指针|| x1米38英寸|指向
long
的指针|| x1米40英寸|指向
float
的指针||
%lf
|指向double
的指针||
%s
个|指向char
的指针[注]|所以没有从
char
和short
到int
的自动转换,就像printf
一样,也没有从float
到double
的自动转换,如果你阅读的是float
,你必须使用%f
,如果你读的是double
,你必须使用%d
。不过,一个好的编译器会再次警告您任何不匹配的情况,找出并注意这些警告是一个非常好的主意!
当你调用
scanf
时,有一个“转换”会自动发生,但它与scanf
无关。如果你使用%s
读取一个字符串,你应该传递一个pointer-to-char
类型的参数,尽管它必须是一个指向一些连续字符的指针。因为scanf
可能不会读取一个字符它可能会读取一个字符串,所以很常见的做法是给scanf
传递一个字符数组,这没关系,因为在C语言的表达式中使用数组时,比如给函数传递数组(如scanf
),使用的值是指向数组第一个元素的指针,它工作正常,并且是scanf
所期望的。和
(请注意,在这种情况下,您 * 不 * 需要在传递给
scanf
的变量上使用&
。)