C语言-输入输出

输入输出

输出

在C语言中,有三个函数是用来输出数据的:

  • puts():只能输出字符串,并且输出后会自动换行。
  • putchar():只能输出单个字符。
  • printf():可以输出各种类型的数据 在这里我们重点介绍printf()函数。

printf()函数

printf()格式控制符的完整形式。

 %[flag][width][.precision]type
  • type表示输出类型。
  • width表示最小输出宽度,也就是最小占几个字符的位置
  • .precision表示输出精度,也就是小数的位数。
    • 当小数的位数大于precision时,会按照四舍五入的原则丢掉多余的字符。
    • 当小数部分的位数小于precision时,会在后面补0。
    • 当用于整数或者字符串时
      1. 用于整数时,.precision表示最小输出宽度。与width不同的是,整数的宽度不足时会在左边补0,而不是补空格。
      2. 用于字符串时,.precision表示最大输出宽度,或者说截取字符串,当字符串长度大于precision时,会截掉多余字符;当字符串小于precision时,.precision将不再起到作用。
    • flag是标志字符。例如,%#x中flag对应#%-9d中flag对应-
标志字符 含 义
- -表示左对齐。如果没有,就按照默认的对齐方式,默认一般为右对齐。
+ 用于整数或者小数,表示输出符号(正负号)。如果没有,那么只有负数才会输出符号。
空格 用于整数或者小数,输出值为正时冠以空格,为负时冠以负号。
# 对于八进制(%o)和十六进制(%x / %X)整数,# 表示在输出时添加前缀;八进制的前缀是 0,十六进制的前缀是 0x / 0X。
对于小数(%f / %e / %g),# 表示强迫输出小数点。如果没有小数部分,默认是不输出小数点的,加上 # 以后,即使没有小数部分也会带上小数点。

有的时候我们并不想程序立即输出结果,我们可以利用一个小的函数将他们的时间控制一下。

  • Mac OS或Linux中的写法为sleep(5);表示程序暂停5秒。
  • Windows中的写法为Sleep(5000);同样表示暂停五秒,但是在Windows中使用毫秒来换算的,并不是直接的5秒。

输入函数

在输入函数我们也同样是三个输入函数:

  • scanf():和printf()类似,scanf()可以输入多类型数据。
  • getchar()、getche()、getch():这三个函数都是用来输入单个字符的。
  • gets():获取一行数据并作为字符串处理,直到遇到回车键才算输入结束。结束后会在字符串最后加一个\0.

scanf()函数

scanf是scan format 的缩写,意思是格式化扫描,也就是从键盘获得用户输入,和printf的功能刚好相反。它的格式和printf()函数不同的是在变量名前要加上一个“&”取地址符,也就是获取变量在内存中的地址。

对于scanf(),输入数据的格式要和控制字符串的格式保持一致。 在输入时我们需要著一些细小的问题,不然程序会提示你出错误,例如:

 #include <stdio.h>
 int main()
 {
  int a, b, c;
  scanf("%d %d", &a, &b);
  printf("a+b=%d\n", a+b);
  scanf("%d   %d", &a, &b);
  printf("a+b=%d\n", a+b);
  scanf("%d, %d, %d", &a, &b, &c);
  printf("a+b+c=%d\n", a+b+c);
 
  scanf("%d is bigger than %d", &a, &b);
  printf("a-b=%d\n", a-b);
  return 0;
 }

它的输入输出是

 10    20↙
 a+b=30
 100 200↙
 a+b=300
 56,45,78↙
 a+b+c=179
 25 is bigger than 11↙
 a-b=14

下面我将它们解释一下。

第一个scanf()的格式控制字符串为"%d %d",中间有一个空格,我们输入时在中间空了很多个空格。第二个scanf(),我们并没有添加很多个空格只在分隔的时候填入了一个空格,它们都一样正确输出了,也就说明,scanf()函数对于空格的管理并不需要严格对应,多几个少几个并没有什么区别。

第三个scanf()的控制字符串为"%d,%d,%d",中间以逗号隔开,所以输入的整数也应该用逗号隔开

第四个scanf()则是要求整数中间以is bigger than隔开。

用户每次按下回车键,程序都会认为完成了一次输入操作,scanf()开始读取用户输入的内容,并根据格式控制字符串从中提取有效数据,只要用户输入的内容和格式控制字符串匹配,就能够正确提取

本质上讲,用户输入的内容都是字符串,scanf()完成的是从字符串提取有效数据的过程。

连续输入

从本质上讲,我们从键盘上输入的数据并没有直接交给scanf()函数,而是放入了缓冲区,直到我们按下回车键,scanf()函数才从缓冲区读取数据。如果缓冲区的数据符合scanf()的要求,那么就代表输入结束;如果不符合要求则会等待用户继续输入,或者干脆判定失败。

例如:

 #include <stdio.h>
 int main()
 {
   int a = 1, b = 2, c = 3, d = 4;  //修改处:给变量赋予不同的初始值
   scanf("%d", &a);
   scanf("%d", &b);
   printf("a=%d, b=%d\n", a, b);
   scanf("%d %d", &c, &d);
   printf("c=%d, d=%d\n", c, d);

   return 0;
 }

结果:

 12 60 a10↙
 a=12, b=60
 c=3, d=4

这个程序说明了scanf()不会跳过不符合要求的数据,遇到不符合要求的数据会读取失败,而不是等待用户继续输入,所以上述程序中的c和d的值并没有发生改变。

所以正是因为缓冲区的存在才能让我们多输入一些数据,或者说一次性输入所有数据,你可以认为这是缓冲区的优势,但正因为这个优势,也造就了它拥有了一些负面影响。

缓冲区

缓冲区⼜称为缓存,它是内存空间的⼀部分。也就是说,在内存空间中预留了⼀定的空间,这些存储空间⽤来缓冲输⼊或输出的数据,这部分预留的空间就叫做缓冲区。

缓冲区根据其对应的是输⼊设备还是输出设备,分为输⼊缓冲区和输出缓冲区。

缓冲区就是⼀块内存区,它⽤在输⼊输出设备和CPU之间,⽤来缓存数据。它使得低速的输⼊输出设备和⾼速的CPU能够协调⼯作,避免低速的输⼊输出设备占⽤CPU,解放出CPU,使其能够⾼效率⼯作。

缓冲区的类型

  缓冲区 分为三种类型:全缓冲、⾏缓冲和不带缓冲。

  1. 全缓冲   在这种情况下,当填满标准I/O缓存后⾏实际I/O操作。全缓冲的典型代表是对磁盘⽂件的读写。
  2. ⾏缓冲   在这种情况下,当在输⼊和输出中遇到换⾏符时,执⾏真正的I/O操作。这时,我们输⼊的字符先存放在缓冲区,等按下回车键换⾏时 才进⾏实际的I/O操作。典型代表是键盘输⼊数据。
  3. 不带缓冲  也就是不进⾏缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显⽰出来。   

缓冲区的刷新

下列情况会引发缓冲区的刷新:

  1. 缓冲区满时;
  2. 执⾏flush语句;
  3. 执⾏endl语句;
  4. 关闭⽂件。

可见,缓冲区满或关闭⽂件时都会刷新缓冲区,进⾏真正I/O操作。

输入其他数据

scanf()不仅能输入整数,还能输入其他数值类型。例如:

#include <stdio.h>
int main()
{
    char letter;
    int age;
    char url[30];
   
    scanf("%c", &letter);
    scanf("%d", &age);
    scanf("%s", url); //可以加&也可以不加&
   
    printf("26个英文字母的最后一个是 %c。\n", letter);
    printf("我的网站建立了有%d个月了,网址是 %s\n", age, url);
    return 0;
}

输出结果:

z↙
6↙
tianalngz.top↙
26个英文字母的最后一个是 z。
我的网站建立了有6个月了,网址是 tianalngz.top

对读取字符串的说明

读取字符串有几个方式:

 char str1[] = "tianlangz.top";
 char* str2 = "学习积累";

这两种方式实际上是有区别的,第一种形式的字符串所在的内存既有读取权限又有写入权限,第二种形式的字符串所在的内存只有读取权限没有写入的权限。而printf()和puts()函数等字符串输出函数只要求对字符串有读取权限就OK了,而scanf()和gets()等字符串输入函数要求字符串有写入权限,所以第一种形式的字符串既可以用于输出函数也可以用于输入函数,第二种只能用于输出函数。

另外,对于第一种形式的字符串,在[]里面要指明字符串最大长度,如果不指明,也可以根据=后面的字符来推算,但是这两个一定要有一个。

这里只做简单介绍。

scanf()函数在读取字符串时以空格分隔,遇到空格就代表当前字符串的结束,所以无法读取含有空格的字符串。

输入单个字符

  1. getchar()

getchar()简单来说就是scanf("%c",c);除了更加简洁没有其他优势了,或者说它只是一个简化版本的scanf字符输入。

  1. getche()

getche()跟scanf()和getchar()最大的区别是,当输入一个字符后,立即读取,并不用等待用户按下回车键。

当然这个函数并不是标准函数,Mac OS和Linux都没有这个函数,这个函数包含在conio.h的头文件中,只有Windows中有这个头文件。

  1. getch()

getch()也没有缓冲区,输入一个字符后立即生效,而它的特别之处在于没有回显,也就是说不会显示输入的字符。

这个函数和getche()函数一样,只有Windows才会默认带这个头文件。

输入字符串

这里主要讲gets()函数,scanf()函数上面已经讲过了。

gets()函数也是有缓冲区的,每次按下回车键,就代表输入结束了,gets()开始从缓冲区读取内容,这一点和scanf()并无不同,而他们俩的主要区别就是:

  • scanf()读取字符串时以空格作为分隔,遇到空格就代表字符串结束了,所以无法读取带有空格的字符串。
  • gets()函数将空格看作字符串的一部分,只有遇到回车键才会结束输入,简单地说就是gets()函数能录入带空格的字符串,scanf()函数不能。

Posts in this Series