本文旨在为c语言初学者讲述一些较为简单的C语言程序的作用,由于此文章需要的是易懂的知识内容,涉及的较为深入的知识博主会在文中标注,并在文章的最后附上相关内容(没有的话就是暂未更新哈哈哈哈)。
printf(“helloworld”)是大部分C语言初学者第一个接触到格式化输出函数的具体程序。他的作用是在屏幕上输出一个helloworld。那么我们就从这个例子了解printf函数的特点。
int main()
{
printf("helloworld");
return 0;
}
1)当我们使用printf函数时,它的结果helloworld会出现在一个小黑屏上,这个小黑屏是vs中用来输出数据到标准输出设备(如显示器)的调试控制台。
2)输出的内容是由printf()中双引号(“”)中的内容决定的。如果“helloworld”,那么输出结果是helloworld。
如果我们希望输出的结果是一个根据程序中的数据来显示的结果,那么我们就需要用到以下方法。
int main()
{
int x = 10;
float y = 3.14f;
printf("x=%d\ny=%f", x, y);
return 0;
}
我们运行发现输出到控制台内容并不完全是双引号(“”)当中的内容,因为%d被替换成了10,%f被替换成了3.140000。\n没有被打印,且中间出现了换行的情况。
我们回顾一下printf函数的全称——格式化输出函数,这两个代码都展示了函数的输出效果,第二个代码展现了格式化的特性。我们接下来将了解到这些格式化的特性。
首先,我们先了解printf函数的一个框架。我们可以将常用的printf函数抽象得到这么一个框架
printf(”格式串”,表达式1,表达式2……)
格式串就是一个可以包含格式化的占位符的字符串,我们可以发现格式串中不仅有普通字符(即直接输出的字符),也有格式字符(即%d,%f这些占位符)普通字符的特性特别简单,即你在格式符中写的字符,程序运行的结果就是将那些字符原封不动的显示出来。格式字符在格式串中起的作用是转换说明,也就是将那些数据以特定的形式打印出来。比如%d是说明在这个位置的数据x要以十进制的形式打印出来,%f是指数据要以定点十进制的形式打印出来。
在格式串中,每一个转换说明都要有一个相应的参数,如上述代码中,%d对应的是x这个整型变量,%f对应的是y这个浮点型变量。那么我们可以发现这个格式化的特点,就是可以将一些数据通过转换说明来输出。
printf的转换说明先简单的介绍几个,具体内容将会更新到别的博客。
%d——将%d对应的表达式数据以整型十进制的形式打印出来。
%f——将%f对应的表达式数据以浮点型定点十进制的形式打印出来。
%c——将%c对应的表达式数据以字符的形式打印出来。
%e——将%e对应的数据(注意这里的数据是浮点型的(float))以科学计数法打印出来。
%s——将%s对应的整个字符串打印出来(这里涉及到指针知识,在此只简述其作用,不解释其原理。)
我们需要知道一个转换说明的具体形式:
%m.nX,如%6.3d。我们用一段代码来展示这个形式转换的作用(具体m,p是整数,x是字母)。
int main()
{
int x = 40;
printf("%6.3d", x);
return 0;
}
我们可以看到这个十进制数40的打印形式为***040(我们用*来具体表现空格符)。这个转换说明的作用为:将这个数放在一个6个字符的空间里,这个数右对齐,这个数的位数是3位(不足往前补0)
接下来我将对转换说明的具体形式(%m.nX)中的作用依次说明。
1)m——这个数决定了这个数据的格式的最小栏宽,最小栏宽决定了这个数据的形式最小是几个字符。以上例来看,40这个数据占用的空间为2,少于我们设置的最小栏宽6,此时计算机为了符合格式,会在这个数据前加空格符号,已达到最小栏宽的设置。(****40)。最小栏宽的设定还可以写为-m,此时数据会往左对齐,不足的数往后补空格。如:(%-4d)此时打印结果为(40****)。
大家可以试试若是数据的位数大于最小栏宽的打印结果,我在评论区会放上例子和打印结果。
2).p——这个数决定了数据的位数,要注意的是,这个数在m的后面,而且无论是否省略m,p前面都要加上一个(.),这个数位作用在整型数据的转换说明和浮点型数据的转换说明是有区别的。我们需要分别探讨。
当这个.p(注意点是不可或缺的)后接的数据是整型数据时,这个数据做的作用是设定这个数的最小数位,上例当中,我们设定整型数据的最小数位是3位,而40数位是2位,低于设定的最小数位,此时需要往前补0,达到我们的设定的3位。
如果后接的是浮点型数据,那么这个.p决定的是保留浮点型数据的小数点位,以一个代码为例。
int main()
{
float y = 3.141592f;
printf("%.4f", y);
}
y是一个有6位小数点位的浮点型数据,%.4f的意思将这个数据转换成保留4位小数的数据(整数部分不影响)。所以打印结果是3.1416.
我们在上文当中还出现了一个换行符(\n),转义字符的作用是实现一些特殊的效果而不会被程序报错。如果我们在格式串中不使用换行符来实现换行,而是通过敲回车键来实现换行的话,程序是会报错的。
由此可以得出转义字符的作用:用来实现一些普通字符不能完成功能。常见的转义字符如下
\n 换行符
\a 蜂鸣符:输出蜂鸣符时,计算机会发出声音
\t 制表符:使用\t会将文本跳到下一个水平制表符的位置,不同的编译器对水平制表符的间隔设置是不一样的,vs当中是8格。比如“s\tb”,那么打印结果是(s*******b),*是空格;
\b 退格符:光标往前移动一个。
scanf的框架与printf是类似的,scanf(”格式串”,表达式1,表达式2……)。要注意的是scanf是将标准输入设备(如键盘)输入的数据输入进程序当中。我们先来分析以下程序,讲解scanf的作用以及输入原理。
既然又提到了格式化这个东西,那么肯定离不开格式串这个东西,前面已经提到了格式串中的不同字符、转换说明的作用和性质。scanf当中的格式串也是同理。
int main()
{
int x, y;
scanf("%d%d", &x, &y);
printf("x=%d y=%d", x, y);
return 0;
}
首先代码的运行结果如下:我们输入的第一个数据被赋予x,第二个数据被赋予y。
这时我们需要解读一下格式串中的内容。scanf的格式串也是可以将内容分为两个部分,一个是普通字符,另一个是转换说明。通常来说,一个scanf的格式串只会包含转换说明,而不会有普通字符,这是由于scanf的读取方式会给普通字符的使用带来不必要的麻烦。这里我们下面再说。
上面的代码可以明显的看出scanf函数的格式串与printf格式串中的表达式有着明显的不同。通常来说,scanf中,转换说明对应的参数都需要有(&)符号,这个符号的作用是得到这个变量的地址,那么scanf的参数为什么会和pritf的参数有所不同呢?
这里就要理解输入和输出的关系差别了。对于输出来说,输的数据都是程序当中出已知的,已经被保存好的,所以当我们需要将这个数据在程序当中使用时,只需要将变量名当做参数即可。但是输入是将数据从输入设备输入进程序中,输入的数据对于程序来说是未知的,程序也不知道要将这些数据保存到什么地方,所以我们需要把变量的地址当成参数来指导这些数据流向什么地方。
用一个形象的例子举例。当我们联系一个在微信中的好友时,我们只需要找到他的微信名,而当我们需要用微信联系一个陌生人时,则需要先通过微信号来添加好友。
我们先来了解一下scanf的读取方式,我们在上例可以感受到一点,就是两个数据中间需要有一个空格隔开。如果用逗号隔开,程序的输出结果就是x的值是正确的·,y的值是一个乱的数据。造成这个结果的原因是scanf函数的读取方式。
我们首先要知道输入的内容会被和格式串依次对比的,上例中20符合%d的格式,被保留,*(空格)不符合%d的格式,被跳过。(我们要知道*和转行不符合标准会被跳过,但是如果是其他字符不符合是会导致读取失败的。)
我们先来引入一个叫做输入缓冲区的概念,我们将这个输入缓冲区简单的当做是一个介于程序和输入设备之间的一个空间,输入缓冲区的作用如下:我们在输入设备输入的数据会放在输入缓冲区中,当我们确定了输入缓冲区的内容时,编译器就会按照scanf中的格式串来将输入缓冲区的数据依次对比。我们以一个程序举例。
int main()
{
int i, j;
float f;
scanf("%d%f%d", &i, &f, &j);
printf("i=%d,f=%f,j=%d", i, f, j);
return 0;
}
我们输入***20+.38e5-8.。看看程序的运行结果(+和3之间有一个点(.),*是空格).
scanf的读取方式如下,首先先按照格式串中内容对比数据,上述程序当中格式串的第一个内容是转换说明%d,那么函数就会在在输入缓冲区依次对比输入字符。首先读取的是*(空格),不符合转换说明,于是接着对比下一个字符,也是*不符合,当读取2时,符合,接着读取0,符合。接着读取+,一个整型数据+不应该在数字后,所以+不符合,scanf函数将读取的20放到第一个参数i中。而不符合的+被放回输入缓冲区。
此时函数接着对比第二个转换说明的内容,+可以是一个浮点型数据的开头,于是保留,接着读取点(.)点也是浮点型数据的一部分,于是保留,接着读取的3和8也是符合要求,被保留。e是用来表示科学计数法的符号,比如1e3的意思就是1x10^3,所以e也是一个浮点型数据的合法内容,被保留。5也是合法数据被保留,接着读取-,-出现在数据后是非法的, 于是scanf停止读取。将.38e5存入浮点型数据f中,-被放回输入缓冲区。
接着读取的是%d,-符合整型的转换说明,被保留,8也符合转换说明,被保留。于是打印结果为:
我们前面提到了scanf函数中的格式串是可以包含普通字符和转换说明的,但是引例当中却没有出现含有普通字符的情况,这是因为使用普通字符会带来不必要的麻烦。
int main()
{
int x, y;
scanf("%d|%d", &x, &y);
printf("x=%d y=%d", x, y);
return 0;
}
我们前面已经了解了scanf的读取形式,它是挨个对比格式串的内容来读取数据的,如果我们输入的内容是30*40(*是空格),那么他就会发生以下情况
我们发现y是读取失败的,这是因为函数在读取完30后,*和|是不匹配的。此时scanf采取的行动不是跳过|,而是直接放弃读取接下来的内容(通常只有*和\n这两个字符会被跳过),然后将剩余内容放回输入缓冲区,只读取30这个符合格式的数据。
那么怎么输入才能成功读取呢,请大家思考并尝试让输入成功与格式串对比成功。我会将正确的输入方式和原理写在评论区。
我们都知道需要用正确的转换说明来符合对应的参数。却不清楚格式转换为什么需要对应相应的数据类型,我们先思考这么一个问题,我们可以用不对应的格式转换吗?答案是肯定的,能,当然能。我们可以用%d读取float型的数据,可以用%d读取char型的数据,也可以用%c来读取int型的数据。我们首先要明白的是一个数据是什么东西。
我们以一个整型数据的100举例,我们要清楚一个数据是会按照二进制数存放在内存当中的,100在内存当中的存储形式为00000000 00000000 00000000 01100100.如果我们用%d的来读取它,那么结果就是整型变量100,如果以%c来读取它,我们会得到的是ASCII码值为100的字符“d”。这是因为‘d’的存储形式和100是一样的(这里如果了解了指针就会明白我说的是笼统的,但是为了方便理解,所以不作过深的解释)。而浮点型的读取是与整型截然不同的读取方法,如果用%f的形式来读取100只会得到0.000000(有兴趣可以去了解一下)。
int main()
{
printf("%d\n", 3.14);//此代码是合法的,且能成功运行
printf("%c\n", 97);
printf("%d\n", 'A');
return 0;
}
请尝试在你的编译器中输入这个代码,并运行查看结果。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。