C语言-数据输入与输出
数据的输入与输出
数据的输入与输出是相对而言,其中:
- 从计算机向外部设备输送数据称为输出(output)。通常的输出设备包括:显示器、打印机等。
- 从外部设备向计算机输送数据称为输入(input)。通常的输入设备包括:键盘、鼠标、扫描仪等。
在C语言中,输入与输出需要使用标准的输入输出库(stdio.h)中的输入函数(scanf())、输出函数(printf())实现,houq可以扩展其他输入输出函数。我们将这种跟硬件打交道的输入输出操作称作外设IO。
库函数已经被编译成了目标文件内置于编译系统,在链接时与编译源程序得到的目标文件相链接,生成可执行程序。
注意:在使用系统库函数的时候,需要用预处理指令#include
将有关的头文件包含到用户资源文件中(一般放在程序的开头位置)头文件中包含了调用函数时需要的有关信息,具体的函数实现在编译的时候再去链接对应的系统库。
输入输出缓冲机制
缓冲区的概念
-
缓冲区(缓存)是内存空间的一部分,用于暂存输入或输出的数据。
-
在进行输入操作时,系统从外部设备(如键盘)读取数据,先放入缓冲区,程序再从缓冲区中读取数据。
输入时数据流向:外部设备 → 输入缓冲区 → 程序
-
在进行输出操作时,系统先将数据放入缓冲区,然后在特定条件下(如缓冲区满、遇到特定字符、手动刷新等)再将缓冲区中的数据输出到外部设备(如屏幕)
输出时数据流向:程序 → 输出缓冲区 → 外部设备
缓冲区的类型
C语言的缓冲区有三种类型:
- **全缓冲:**当缓冲区填满后,才进行实际的输入输出操作。例如:对磁盘文件的读写。— window全缓冲区大小是4096字节, Linux全缓冲区大小是1024字节。
- 行缓冲:当在输入和输出中遇到换行符(\n或者回车)时,执行实际的输入输出操作。例如:标准输入(stdin)和标准输出(stdout)。
- **无缓冲:**不进行缓冲,直接进行输入输出操作。例如:标准错误流(stderr)
缓冲区的刷新条件
缓冲区的刷新(如将缓冲区中的数据实际输出到外部设备)通常发生在以下情况:
- **缓冲区满:**当缓冲区写满时,会自动刷新。
- **遇到特定字符:**如换行符(\n)等。
- **手动刷新:**使用fflush(stdout)函数手动刷新输出缓冲区。
- **程序关闭时:**当程序结束时,缓冲区中的数据会被刷新。
缓冲区的实际应用
- **提高效率:**通过缓冲区,可以减少与外部设备的交互次数,提高数据传输的效率
- **处理输入输出:**例如:使用scanf和printf函数时,数据先被放入缓冲区,然后按照特定的规则从缓冲区读取或输出。
原理实现
简单的输入与输出
用printf函数输出数据
基础用法
语法:
printf("格式控制",输出列表);
引入文件:
#include <stdio.h>
举例:
printf("i=%d,x=%d,y=%d\n",i,34,i+1);
注意:格式控制中的格式化符号(%d)要和输出的数据一一对应。
参数:
- ==格式控制:==用一对英文双引号括起来,包括两种信息:
- 格式说明:由
%
和特定字符
组成,如:%d,%f,%c等,这是格式说明符,用于说明输出项目所采用的格式。 - 普通字符:作为说明性文字、符号等,按照输入原样输出。
- 格式说明:由
- ==输出列表:==输出列表中的各项目指出了所要输出的内容。可以是常量(字面量、符号常量、const修饰的变量)、变量、表达式。输出列表的个数,由格式控制中的格式化符号来决定。
基本的格式化符
%d
按有符号十进制整型(int)数据的实际长度输出。(十进制(0)、八进制(00)、十六进制(0x00))
%u
按无符号十进制整型(int)数据的实际长度输出。
%c
仅输出一个字符(char)
%s
输出结果是字符串,举例:printf(“%s\n”,“CHINA”);,输出结果:CHINA
%f
以小数形式输出float
类型实数;
%lf
以小数形式输出double
类型(双精度)实数
%e
也可以写作%E,以指数形式输出一个实数(涵单双精度)。小数点前1位非0数字,并输出6位小数。
%hd
短整型(short int/short)
%hhd
字符型的ASCII码,char数据对应的ASCII码的值,举例:char a = ‘A’;printf(“%hhd\n”,a);,输出结果:65
%ld
长整型(long int/long)
%lld
长长整型(long long int/long long)
%x
十六进制,但是十六进制的前缀0x不会打印出来,举例:printf(“%x\n”,198);,输出结果:c6
%#x
十六进制,并且十六进制的前缀0x也会打印出来,举例:printf(“%#x\n”,198);,输出结果:0xc6
%o
八进制,前缀0不会打印出来,举例:5
%#o
八进制,并且八进制的前缀0也会打印出来,举例:05
%p
打印内存的地址
%%
输出%本身
案例:
#include <stdio.h>int main(int argc,char *argv[])
{printf("%x,%#x\n",255,255);// 输出16进制整数printf("%o,%#o\n",255,255);// 输出8进制printf("%d,%u,%c\n",5,5,'%');// 正常输出printf("%%\n");// 输出 %printf("%f,%lf,%e\n",99.12345,99.12345,99.12345);// 输出小数return 0;
}
用scanf函数输入数据
基础用法
语法:
scanf("格式控制", 地址列表);
注意:地址列表不能传变量、常量、表达式。只能传与之对应的内存地址(首地址),如果是普通的变量,通过&+b变量名
获取变量地址。
举例:
int a = 10; // 定义了一个变量a
scanf("%d",&a);// &在这里称作 取地址符,&a的意思是获取变量a对应的内存地址(首地址)
作用:
将从键盘输入的数据存入内存中所占的存储单元里,存储单元有地址标识。
参数:
- 格式控制:含义等同于printf函数的格式控制,说明输入的数据应该使用的格式。
- 地址列表:是由若干个地址组成,可以是变量的地址或者字符串的地址。&是取地址符号,用于取出变量的地址。与格式化输出一样,在格式控制中,用于说明数据格式的格式说明符以%开头,后面紧跟具体的格式。
案例:
-
需求:从键盘输入整数给变量a,b,c赋值
-
代码:
#include <stdio.h>int main(int argc,char *argv[]) {// 定义三个变量a,b,c,用来接收控制台的输入int a,b,c; // 因为我们需要通过接收控制台输入进行赋值,所以无需初始化// 每次在使用键盘录入前加上提示信息printf("请输入3个整数:\n");scanf("%d%d%d",&a, &b, &c);// 测试输出printf("a=%d,b=%d,c=%d\n", a, b, c);return 0; }
-
运行结果:
总结:输入多个数据的时候可以用空格、Tab键、回车键中的任意一种。
说明:
-
scanf函数中的“格式控制”后面应当是变量的地址,由取地址符&和变量名共同组成,举例:
scanf("%f%d", &a, &b);
-
如果“格式控制”中除了格式说明以外还有其他字符,则在输入数据时必须在对应位置输入与之相同的字符,举例:
scanf("%d,%d",&a, &b);
从键盘录入数据的时候,使用格式控制中的逗号分隔:
3,4
scanf("%d-%d-%d",&year, &month, &day);
从键盘录入数据的时候,使用格式控制中的短横线分隔:
2025-7-11
scanf("%d年%d月%d日",&year, &month, &day);
从键盘录入数据的时候,使用格式控制中的年月日分隔:
2025年7月11日
-
用
%c
格式输入字符时,转义符(如:\n
,\t
等)都作为有效字符输入,应注意:scanf("%c%c%c", &a, &b, &c);
注意:其实Tab键、空格键、回车键的响应都是当做字符处理。
-
在输入数值型数据(整型+浮点型)时,遇到空格、回车、Tab键或遇到非法输入(A,?..),则认为该输入结束;
scanf("%d%d%d",&a, &b, &c);
-
对于
unsigned
型变量所需的数据,建议用%u
格式输入;若用%d
(signed int
),需确保输入值在int
范围内(否则可能溢出)
复杂的输入与输出
按指定格式输出数据的宽度、小数位数、上下行数据按小数点对齐、用八进制、十六进制输出等。
输出数据格式控制
整型格式说明符
-
十进制形式(0~9)
说明符 说明 数据类型 %d和%md 用于基本整型 int %ld和%mld 用于长整型 long %u和%mu 用于无符号基本整型 unsigned int %lu和%mlu 用于无符号长整型 unsigned long -
八进制形式(0~7)
说明符 说明 数据类型 %o和%mo 用于基本整型 int %lo和%mlo 用于长整型 long -
十六进制形式(0~F)
说明符 说明 数据类型 %x和%mx 用于基本整型 int %lx和%mlx 用于长整型 long
说明:
m
表示输出整型数据所占的总宽度(列数,1个字符占1列),其中:
① 当实际数据的位数不到m位时,数据前面将用空格填满, 举例:原数据("12"
),列宽为m(4)的数据(" 12"
)
② 若实际数据的位数大于等于m位时,则以数据的实际位数为标准进行输出,列宽无效,举例:
原数据("12345"
),列宽m(4)的数据("12345"
)
总结:
如果实际 数据列宽 < m,使用空格补齐m。
如果实际 数据列宽 >=m,输出实际数据,m失效。
一个int型整数也可以用%u输出,反之一个unsigned型整数也可以用%d、%o、%x格式输出。按相互赋值的规则处理。
举例:
#include <stdio.h>int main(int argc,char *argv[])
{printf("%d\n",12); // "12" 输出后,实际列宽是2printf("%6d\n",12); // " 12" 输出后,实际列宽是6,右对齐,左边使用空格填充printf("%-6d\n",12); // "12 " 输出后,实际列宽是6,左对齐,右边使用空格填充printf("%6d\n",1234567); // "1234567" 输出后,实际列宽是7,一旦实际列宽超过指定列宽,就按实际列宽输出printf("%06d\n",12); // "000012" 输出后,实际列宽是6,右对齐,左边使用0填充printf("%+d,%+d\n",12,-12); // "+12,-12" 输出后,显示正负号printf("%#06x,%#06o\n",12,12);// "0x000c,000014" 输出后,实际列宽是6return 0;
}
运行效果:
字符型格式说明符
-
字符型
说明符 说明 举例 %c或者%mc 输出的字符占m列 printf(“%3c\n”,‘a’);
用法和整型的用法一致。
-
字符串型
在C语言中,支持字符串常量,不支持字符串变量。
说明符 说明 %ms 输出的字符串占m列。若串长>=m,全部输出;反之在串前补空格(m为正往前补空格) 举例: printf("%6s\n","hello");
" hello"
%-ms 输出的字符串占m列。若串长>=m,全部输出;反之在串后补空格(m为负往后补空格) 举例: printf("%-6s\n","hello");
"hello "
%m.ns 输出的字符串占m列,取字符串前 n 个字符,再按 m 列补空格。 举例: printf("%6.2s\n","hello");
" he"
(先截取,在补全)%-m.ns 输出的字符串占m列。只取字符串前n个字符,不足部分串后补空格。 举例: printf("%-6.2s\n","hello");
"he "
案例:
-
需求:字符串输出
-
代码:
#include <stdio.h>int main(int argc,char *argv[]) {printf("%3s\n%7.2s\n%-5.3s\n%.4s\n","CHINA","CHINA","CHINA","CHINA");// %3s: "CHINA" 超出指定列宽,按实际列宽输出// %7.2s: " CH" 截取CH,实际列宽补足到7,右对齐// %-5.3s: "CHI " 截取CHI,实际列宽补足到5,左对齐// %.4s: "CHIN" 截取CHIN,实际列宽就是默认列宽4return 0; }
-
运行效果:
浮点型格式说明符
浮点型格式分为三种形式:
序号 | 名称 | 说明 |
---|---|---|
1 | 十进制形式 | %m.nf 或者%-m.nf ,m是列宽,n是保留的小数位 |
2 | 指数形式 | %m.ne 或者%-m.ne ,m是列宽,n是保留的尾数位 |
3 | %g或者%G形式 | 根据数值的大小,自动选择%f或者%e中宽度较短的一种格式,不输出无意义的0 |
解释:
在输出浮点型数据时,格式说明符中的m表示整个数据所占的列宽,n表示小数点后面所占的位数(保留的小数位)
如果在小数点后取n位后,所规定的数据宽度m不够输出数据前面的整数部分(包括小数点),则按实际的位数进行输出。
案例:
-
需求:输出浮点型数时,指定小数位。
-
案例:
#include <stdio.h>int main(int argc,char *argv[]) {float f = 123.456f; // f是标识,不计入列宽的,实际列宽是7printf("%8.2f\n%-8.2f\n%8.6f\n%9.2e\n%g\n%G\n",f,f,f,f,f,f);// %8.2f: " 123.46" 小数位保存采取“四舍五入” 小数保留2位,实际列宽补足到8,右对齐// %-8.2f: "123.46 " 左对齐// %8.6f: "123.456001" 实际列宽超过指定列宽,以实际列宽为准// %9.2e: " 1.23e+02" 首先,小数转换为指数;接着,处理尾数,这里的.2是尾数位;最后补足列宽。return 0; }
-
运行结果
注意:这里的小数位的保留采用四舍五入
案例:
-
要求:求3个圆的周长,输出结果时要求按小数点对齐,取两位小数。
-
代码:
#include <stdio.h>#define PI 3.14int main(int argc,char *argv[]){double r1 = 1.53, r2 = 21.83, r3 = 123.71, s1,s2,s3;s1 = 2.0 * PI * r1;s2 = 2.0 * PI * r2;s3 = 2.0 * PI * r3;printf("%6.2f\n%6.2f\n%6.2f\n",s1, s2, s3);return 0;}
-
运行结果:
案例:
-
要求:设有如下C程序
-
代码
#include "stdio.h"main(){ double x=34.567;printf("x=%f\n",x);// 34.567000printf("x=%d\n",x);// 27263,这种写法错误,自动类型转换异常printf("x=%d\n",(int)x);// 34}
-
这个程序的实际运行结果为
x=34.567000 x=27263 x=34
-
说明
显然,这个程序中的第二个格式输出语句输出的结果是错误的,这是因为在第二个格式输出语句中,格式说明符%d是基本整型格式说明符,而输出项目是双精度型的数据,它们是不匹配的。
说明符 说明 数据类型 %f或者%e 用于单精度浮点型 float %lf 用于双精度浮点型 double 总结:
① 与输出不同,输入时无论是单精度还是双精度浮点型,都不能用m.n来指定输出的宽度和小数点后保留的位数。
② 可以指定输入数据所占的列数,系统自动按它截取所需的数据,如:
scanf("%3d%3d",&a, &b);
输入测试数据:
1234567
结果:
a=123,b=456
解释:从输入的数据1234567中,截取前3列(123)赋值给a,再截取3列(456)赋值给b,剩余的7舍弃
③ 若在%后有一个
*
和一个数字,表示跳过指定的列数,如:scanf("%2d%*3d%3d",&a, &b);
输入测试数据:
12345678
结果:
a=12,b=678
解释:从输入的数据12345678中,截取前2列(12)赋值给a,再跳过3列(345),然后截取3列(678)赋值给b
案例:
#include <stdio.h>int main(int argc,char *argv[]) {int a, b;printf("通过控制台输入一个数:\n");// 这里的m不是列宽,是截取的列数,%3d,就是截取输入内容的前3个字符scanf("%3d%3d",&a, &b);printf("%d,%d\n",a,b);// 这里的%*跳过,%*3d,跳过3列 12345678printf("通过控制台输入一个数:\n");scanf("%2d%*3d%3d",&a, &b);//scanf("%2d%*d%3d",&a, &b); //错误写法,不能没有列宽printf("%d,%d\n",a,b);return 0; }
运行结果
说明:
当用于输入整型数据的格式说明符中没有宽度说明时,则在具体输入数据时分为以下两种情况:
① 如果各格式说明符之间没有其它字符,则在输入数据时,两个数据 之间用"空格"、或"Tab"、或"回车"来分隔。
② 如果各格式说明符之间包含其它字符,则在输入数据时,应输入与 这些字符相同的字符作为间隔。
例如,设有如下说明
int a,b;
float c,d;
现要利用格式输入函数输入
a=12,b=78,c=12.5,d=7.6
。 采用不同的格式说明,其输入数据的形式也是不同的。分别说明如下:-
若输入语句为
scanf("%d%d%f%f",&a,&b,&c,&d);
(即格式说明符中没有宽度说明,各格式说明符之间也没有其他字符。)则输入数据的形式应为
12 78 12.5 7.6
↲ (两个数据之间用空格来分隔,当然也可用“Tab”或“回车”来分隔。) -
若输入语句为
scanf("%d,%d,%f,%f",&a,&b,&c,&d);
(格式说明符中没有宽度说明,但各格式说明符之间有其它字符,即逗号)则输入数据的形式应为
12,78,12.5,7.6
↲ (即在输入的两个数据之间同时要输入逗号。) -
若输入语句
scanf("a=%d,b=%d,c=%f,d=%f",&a,&b,&c,&d);
(即格式说明符中没有宽度说明,但各格式说明符之间有其它字符。)输入数据的形式应为
a=12,b=78,c=12.5,d=7.6
↲ (即在输入的两个数据之间同时要输入这些非格式说明符的字符。) -
在用于输入的实型格式说明符中不能用m.n来指定输入的宽度和小数点后的位数(这是与输出的不同之处)。
例如:
scanf("%7.2f",&a); × 此用法是错误的
-
为了便于程序执行过程中从键盘输入数据,在一个C程序开始执行时,系统就在计算机内存中开辟了一个输入缓冲区,用于暂存从键盘输入的数据。开始时该输入缓冲区是空的。当执行到一个输入函数时,就检查输入缓冲区中是否有数据。
补充
说明符 含义 示例 %n 已读取的字符数 scanf(“%d%n”, &num, &count); 案例:
-
代码:
#include <stdio.h>int main(int argc,char *argv[]) {int num,count; // count,用来保存输入数据列宽(统计输入的字符个数)printf("请输入一个数据:\n");scanf("%d%n",&num,&count);printf("num=%d,count=%d\n",num,count);return 0; }
-
运行结果
-