NOIP学习小站
西安交通大学附属中学航天学校

scanf输入与printf输出

输入输出除了使用iostream头文件中的cin和cout外,还可以使用cstdio头文件中的scanf和printf函数。两者各有优缺点,scanf和printf在适当的场合使用可能收到意想不到的效果。

scanf和printf是C语言风格的输入输出,scanf(scan+format)和printf(print+format)表示的是格式化(format)输入输出。这两个函数用来输入输出数据时,最大的特点就是要使用表示格式占位符实现灵活多变的格式化。

一、printf格式化输出

回到之前的浮点数保留小数位数的处理方法,使用cout处理很复杂:要引入新的头文件iomanip,使用cout<<fixed<<setprecision(5);这样难以记忆的语句!

#include<iostream> 
#include<iomanip>
using namespace std;
int main()
{    
    cout<<fixed<<setprecision(5);    
    cout<<12.3456*34.5678<<endl;
    return 0;
} 

下面是使用printf格式化输出的方法,因为不再需要cout,所以不需要引入头文件iostream,不需要使用std命名空间,但是需要引入头文件cstdio:

#include<cstdio>
int main()
{
    printf("%.5f",12.3456*34.5678);  //%.5f是占位符,表示保留5位小数的float数据
    //上面的printf输出:1个占位符,1个要输出的数据(这里是一个计算表达式)
    return 0;
} 

经过对比发现使用printf输出指定小数位数的浮点数比起cout简单得多。再通过几个例子来分析printf格式化输出函数的使用方法:

#include<cstdio> 
int main()
{
	char ch = 'A';
	printf("%c %d",ch,ch);    //%c和%d都是占位符,分别表示char数据和int数据
	//上面的printf输出:2个占位符,2个要输出的数据(这里都是变量ch)
	//程序会输出A 65(占位符中间的空格输出时原样出现)
    return 0;
} 
#include<cstdio> 
int main()
{
	int a = 123,b = 456;     //定义两个int型变量a、b并都赋初值
	printf("%d+%d=%d",a,b,a+b);  //%d是占位符,表示int数据
	//上面的printf输出:3个占位符(都是%d),3个要输出的数据(int类型变量a、b和计算表达式a+b)
	//程序会输出123+456=579:变量a的值出现在第一个%d的位置,变量b的值出现在第二个%d的位置,a+b的计算结果出现在第三个%d的位置,其它占位符之外的字符('+'、'=')原样输出。
    return 0;
} 

通过上面的测试,可以小结printf函数的使用方法:printf("包含占位符的字符串",变量/表达式列表)

  1. 第二部分变量/表达式列表是用,分隔开的若干个变量或者表达式;
  2. 第一部分包含占位符的字符串中占位符的数量与第二部分变量或者表达式的数量一致;
  3. 输出时包含占位符的字符串中的占位符会被第二部分对应位置的变量或者表达式计算结果按照占位符指定的格式依次替代(占位符“占位”的由来);
  4. 包含占位符的字符串中占位符之外的字符原样输出;
  5. 第一部分"包含占位符的字符串"中可以没有任何占位符,此时没有第二部分:printf("Hello World");

二、常见输入输出占位符

占位符说明
\(\%d\)用于\(int\)类型
\(\%nd\) (\(n\)是正整数,例如\(\%4d\))至少\(n\)位的\(int\)类型,如果不足\(n\)位,前面补空格直到补齐\(n\)位
\(\%-nd\) (\(n\)是正整数,例如\(\%-4d\))至少\(n\)位的\(int\)类型,如果不足\(n\)位,后面补空格直到补齐\(n\)位
\(\%0nd\) (\(n\)是正整数,例如\(\%04d\))至少\(n\)位的\(int\)类型,如果不足\(n\)位,前面补0直到补齐\(n\)位
\(\%lld\)用于\(long\ long\)类型
\(\%f\)用于\(float\)类型,输出时默认6位小数
\(\%lf\)用于输入\(double\)类型;\(double\)类型输出时建议使用\(\%f\)
\(\%.nf\) (\(n\)是正整数,例如\(\%.2f\))用于输出\(n\)位小数的\(float\)类型,会自动四舍五入
\(\%m.nf\) (\(m\)、\(n\)是正整数,例如\(\%5.2f\))用于输出\(n\)位小数的\(float\)类型,算上小数点至少占\(m\)位
\(\%c\)用于\(char\)类型
\(\%s\)用于字符串
#include<cstdio>
int main()
{
	printf("Hello World\n");
	printf("%5d\n",5);
	printf("%2d\n",12345);
	printf("%-5dend\n",5);
	printf("%05d\n",5);
	printf("%+05d\n",5);
	printf("%.4f\n",3.14159);
	printf("%8.4f\n",3.14159);
	printf("%-8.4fend\n",3.14159);
	printf("%08.4f\n",3.14159);
	printf("%c %d\n",'a','a');
	printf("%s","Hello World");  //后续会学习字符串
    return 0;
} 

运行结果如下:

Hello World
    5
12345
5    end
00005
+0005
3.1416
  3.1416
3.1416  end
003.1416
a 97
Hello World

三、scanf格式化输入

scanf格式化输入用来将指定格式的若干数据输入到对应数量的变量中,和printf相似,scanf的使用方法:scanf("包含占位符的字符串",变量列表); 不过要注意的是第二部分是若干用,隔开的变量列表,每个变量名前要添加一个特殊的&运算符号。下面通过几个测试程序来加以说明scanf的使用:

#include<cstdio>
int main()
{
    int n;
    scanf("%d",&n);		//变量名n前必须有&运算符
	printf("%d",n*n); 
    return 0;
} 
#include<cstdio>
int main()
{
    int a,b;
    scanf("%d%d",&a,&b);	//占位符%d%d中间也可以加空格:scanf("%d %d",&a,&b);
	printf("%d+%d=%d",a,b,a+b); 
    return 0;
} 
#include<cstdio>
int main()
{
	const double PI = 3.14159;
    double r,C,S;
	scanf("%lf",&r);
	C = 2 * PI * r;
	S = PI * r * r;
	printf("R=%f\nC=%.5f\nS=%.5f",r,C,S);
    return 0;
} 

cin/cout,scanf/printf各有优势。相比较cout,printf更容易通过占位符实现丰富多样的格式化。一般来说,使用scanf读入同样的数据的速度要快于cin,数据量较大(例如要读入百万级别的数据)时差距会相当明显。但大多数情况下程序输入的数据不会很多,cin和scanf输入数据耗时差异不明显。而使用cin、cout输入输出时不用考虑不同数据类型的占位符,相对方便。

此外,不建议在程序里混合使用cin/cout与scanf/printf。也可以通过关闭同步的方式加快cin的读入速度,不过此时不能再使用scanf输入数据:

#include<iostream>
using namespace std;
int main(){
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	//其它程序代码 
    return 0;
}

四、输入char注意事项

通过前面的学习我们已经知道,输入数据时多个数据间可以用若干空格或者回车分隔开(空格和回车是输入数据的分隔符)。但是如果要用scanf来输入本来就包括字符的数据的时候,要格外小心!

#include<iostream>
using namespace std;
int main()
{
    char c1,c2;
    cin>>c1>>c2;
    cout<<c1<<" "<<c2<<endl;
    return 0;
}

使用cin输入char字符时,仍然满足上面输入规则,输入的两个char字符间可以有若干的空格、回车。例如这里输入字符a和b,中间随意输入若干的空格回车,ch2变量都能正常接收到分隔符后的字符,运行结果如下:

a b

#include<cstdio>
int main()
{
    char c1,c2;

    scanf("%c%c",&c1,&c2);

    printf("%c %c\n",c1,c2);
    printf("%d %d",c1,c2);
    return 0;
}

使用scanf输入char时,和cin有区别,这里使用\(\%c\%c\)作为占位符,输入时两个char间不能有任何分隔符,否则输入到c2中的字符就变成中间的分隔符。使用getchar()函数输入字符与这里的情况相同。

左边程序,如果输入ab,运行结果如下:

a b
97 98

可知此时ch2接收到了字符'b'


如果输入:a b,运行结果如下:

a
97 32

此时,ch2接收到的字符是a b中间的空格。

五、scanf和printf的特殊应用

#include<cstdio>
int main()
{
    int a,b,c;
    scanf("%1d%1d%1d",&a,&b,&c);
    printf("%d %d %d",a,b,c);
    return 0;
} 

scanf输入时使用了3个特殊的占位符\(\%1d\),\(\%nd\)用于输出时表示至少占n位。这里用于输入,那么表示输入的整数占n位。输入一个三位数123,运行结果如下所示:

1 2 3
#include<cstdio>
int main()
{
    double num;  //要处理的浮点数 
    int n;       //输出时保留的位数
	//输入要处理的浮点数和保留的位数 
    scanf("%lf%d",&num,&n);
    printf("%.*f",n,num);
    return 0;
}

输出时使用了\(\%.*f\),表示保留的小数位数值由后面的输出列表对应位置的值决定。

输入数据:
3.14159 3
运行结果:
3.142