第4章 字符串和格式化输入/输出
printf : print format, 格式化打印的意思。
#include <stdio.h>
#include <string.h> // 提供strlen()函数的原型
# define DENSITY 1000 //人体密度 1000kg/立方米
int main(void) {
char name[50]; // 可以容纳50个字符的数组
printf("你的名字?\n");
scanf("%s", name); // 默认使用 ASCII编码
float weight, volume;
printf("%s,你的体重是多少公斤?\n", name);
scanf("%f", &weight);
volume = weight / DENSITY;
int size, letters;
size = sizeof(name);
letters = strlen(name);
printf("%s,你的体积是%.2f 立方米\n", name, volume);
printf("你的名字在计算存储中对应%d个字符存储空间,我们至少需要用%d个字节存储你的名字", letters, size);
return 0;
}
/*
你的名字?
Zhang San
Zhang,你的体重是多少公斤?
Zhang,你的体积是0.00 立方米
你的名字在计算存储中对应5个字符存储空间,我们至少需要用50个字节存储你的名字
*/
存在的问题:
- clion中,命令行运行测试默认不能输入中文,解决方式:run,Modify Run Configuration,选中 Emulate terminal in the output console
- scanf默认遇到空格会结束输入,解决方式:
scanf("%[^\n]",....
- scanf函数中,
[
用来扫描特定的字符集。如果它以^开头,表示扫描除了字符集以外的所有字符。
- scanf函数中,
#include <stdio.h>
#include <string.h> // 提供strlen()函数的原型
# define DENSITY 1000 //人体密度 1000kg/立方米
int main(void) {
char name[50]; // 可以容纳50个字符的数组
printf("你的名字?\n");
scanf("%[^\n]", name); // 接受非 \n 输入
float weight, volume;
printf("%s,你的体重是多少公斤?\n", name);
scanf("%f", &weight);
volume = weight / DENSITY;
int size, letters;
size = sizeof(name);
letters = strlen(name);
printf("%s,你的体积是%.4f 立方米\n", name, volume);
printf("你的名字在计算存储中对应%d个字符存储空间,我们用%d个字节存储你的名字", letters, size);
return 0;
}
/*
你的名字?
Zhang San
Zhang San,你的体重是多少公斤?
77.777
Zhang San,你的体积是0.0778 立方米
你的名字在计算存储中对应9个字符存储空间,我们用50个字节存储你的名字
*/
程序说明:
- 用数组(array)储存字符串(characterstring)。在该程序中,用户输入的名被储存在数组中,该数组占用内存中50个连续的字节,每个字节储存一个字符值。
- 使用%s 转换说明来处理字符串的输入和输出。注意,在 scanf()中,name 没有&前缀,而 weight有( &weight和name都是地址)。
- 用C预处理器把字符常量DENSITY定义为1000。
- 用C函数strlen()获取字符串的长度。
使用中文名字的结果:
#include <stdio.h>
#include <string.h> // 提供strlen()函数的原型
# define DENSITY 1000 //人体密度 1000kg/立方米
int main(void) {
char name[50]; // 可以容纳50个字符的数组
printf("你的名字?\n");
scanf("%[^\n]", name); // 接受非 \n 输入
float weight, volume;
printf("%s,你的体重是多少公斤?\n", name);
scanf("%f", &weight);
volume = weight / DENSITY;
int size, letters;
size = sizeof(name);
letters = strlen(name);
printf("%s,你的体积是%.4f 立方米\n", name, volume);
printf("你的名字在计算存储中对应%d个字符存储空间,我们用%d个字节存储你的名字", letters, size);
return 0;
}
/*
你的名字?
张三
张三,你的体重是多少公斤?
75
张三,你的体积是0.0750 立方米
你的名字在计算存储中对应4个字符存储空间,我们用50个字节存储你的名字
*/
字符串
-
字符串是一个或多个字符的序列,用双引号标识。
-
C语言将字符串存储在char数组中,用空字符
\0
标识字符串的结束,因此char数组的大小至少比要存储的字符数多1,预留一个给空字符 -
scanf会处理添加空字符到字符串结尾
常量和C预处理器
符号常量的定义,注其中并没有=符号,其通用格式如下:
#define NAME value
const 关键字: 用于限定变量只读, 比 #define
更灵活
limits.h 中的一些常量
明示常量 | 含义 |
---|---|
CHAR_BIT | char 类型的位数 |
CHAR_MAX | char类型的最大值 |
CHAR_MIN | char类型的最小值 |
SCHAR_MAX | signed char类型的最大值 |
SCHAR_MIN | signed char类型的最小值 |
UCHAR_MAX | unsigned char类型的最大值 |
SHRT_MAX | short类型的最大值 |
SHRT_MIN | short类型的最小值 |
USHRT_MAX | unsiqned short类型的最大值 |
INT_MAX | int 类型的最大值 |
INT_MIN | int类型的最小值 |
UINT_MAX | unsigned int的最大值 |
LONG_MAX | long 类型的最大值 |
LONG_MIN | long 类型的最小值 |
ULONG_MAX | unsigned long 类型的最大值 |
LLONG_MAX | long long 类型的最大值 |
LLONG_MIN | long long 类型的最小值 |
ULLONG_MAX | unsigned long long类型的最大值 |
#include <stdio.h>
#include <limits.h>
int main(void) {
printf("CHAR_BIT,char 类型的位数:%d\n",CHAR_BIT);
printf("CHAR_MAX,char类型的最大值:%d\n",CHAR_MAX);
printf("CHAR_MIN,char类型的最小值:%d\n",CHAR_MIN);
printf("SCHAR_MAX,signed char类型的最大值:%d\n",SCHAR_MAX);
printf("SCHAR_MIN,signed char类型的最小值:%d\n",SCHAR_MIN);
printf("UCHAR_MAX,unsigned char类型的最大值:%d\n",UCHAR_MAX);
printf("SHRT_MAX,short类型的最大值:%d\n",SHRT_MAX);
printf("SHRT_MIN,short类型的最小值:%d\n",SHRT_MIN);
printf("USHRT_MAX,unsiqned short类型的最大值:%d\n",USHRT_MAX);
printf("INT_MAX,int 类型的最大值:%d\n",INT_MAX);
printf("INT_MIN,int类型的最小值:%d\n",INT_MIN);
printf("UINT_MAX,unsigned int的最大值:%llu\n",UINT_MAX);
printf("LONG_MAX,long 类型的最大值:%d\n",LONG_MAX);
printf("LONG_MIN,long 类型的最小值:%d\n",LONG_MIN);
printf("ULONG_MAX,unsigned long 类型的最大值:%llu\n",ULONG_MAX);
printf("LLONG_MAX,long long 类型的最大值:%lld\n",LLONG_MAX);
printf("LLONG_MIN,long long 类型的最小值:%lld\n",LLONG_MIN);
printf("ULLONG_MAX,unsigned long long类型的最大值:%llu\n",ULLONG_MAX);
return 0;
}
/*
CHAR_BIT,char 类型的位数:8
CHAR_MAX,char类型的最大值:127
CHAR_MIN,char类型的最小值:-128
SCHAR_MAX,signed char类型的最大值:127
SCHAR_MIN,signed char类型的最小值:-128
UCHAR_MAX,unsigned char类型的最大值:255
SHRT_MAX,short类型的最大值:32767
SHRT_MIN,short类型的最小值:-32768
USHRT_MAX,unsiqned short类型的最大值:65535
INT_MAX,int 类型的最大值:2147483647
INT_MIN,int类型的最小值:-2147483648
UINT_MAX,unsigned int的最大值:4294967295
LONG_MAX,long 类型的最大值:2147483647
LONG_MIN,long 类型的最小值:-2147483648
ULONG_MAX,unsigned long 类型的最大值:4294967295
LLONG_MAX,long long 类型的最大值:9223372036854775807
LLONG_MIN,long long 类型的最小值:-9223372036854775808
ULLONG_MAX,unsigned long long类型的最大值:18446744073709551615
*/
float.h 中的一些常量:
明示常量 | 含义 |
---|---|
FLT_MANT_DIG | float 类型的尾数位数 |
FLT_DIG | float类型的最少有效数字位数(十进制) |
FLT_MIN_10_EXP | 带全部有效数字的float类型的最小负指数(以10为底) |
FLT_MAX_10_EXP | float类型的最大正指数(以10为底) |
FLT_MIN | 保留全部精度的 float类型最小正数 |
FLT_MAX | float 类型的最大正数 |
FLT_EPSILON | 1.00和比1.00大的最小float 类型值之间的差值 |
#include <stdio.h>
#include <float.h>
int main(void) {
printf("FLT_MANT_DIG,float 类型的尾数位数:%d\n", FLT_MANT_DIG);
printf("FLT_DIG,float类型的最少有效数字位数(十进制):%d\n", FLT_DIG);
printf("FLT_MIN_10_EXP,带全部有效数字的float类型的最小负指数(以10为底):%e\n", FLT_MIN_10_EXP);
printf("FLT_MAX_10_EXP,float类型的最大正指数(以10为底):%e\n", FLT_MAX_10_EXP);
printf("FLT_MIN,保留全部精度的 float类型最小正数:%e\n", FLT_MIN);
printf("FLT_MAX,float 类型的最大正数:%e\n", FLT_MAX);
printf("FLT_EPSILON,1.00和比1.00大的最小float 类型值之间的差值:%e\n", FLT_EPSILON);
return 0;
}
/*
FLT_MANT_DIG,float 类型的尾数位数:24
FLT_DIG,float类型的最少有效数字位数(十进制):6
FLT_MIN_10_EXP,带全部有效数字的float类型的最小负指数(以10为底):2.121996e-314
FLT_MAX_10_EXP,float类型的最大正指数(以10为底):1.877449e-322
FLT_MIN,保留全部精度的 float类型最小正数:1.175494e-38
FLT_MAX,float 类型的最大正数:3.402823e+38
FLT_EPSILON,1.00和比1.00大的最小float 类型值之间的差值:1.192093e-07
*/
printf()
虽然 printf()是输出函数,scanf()是输入函数,但是它们的工作原理几乎相同。两个函数都使用格式字符串和参数列表。
printf()函数的格式:
printf(格式字符串,待打印项1,待打印项2....);
printf函数提供了数据转换说明,指定如何把相关的数据转换成可显示的形式。printf函数转换说明及其打印的输出结果见下表:
转换说明 | 输出 |
---|---|
%a | 浮点数、十六进制数和p记数法(C99/C11) |
%A | 浮点数、十六进制数和p记数法(C99/C11) |
%C | 单个字符 |
%d | 有符号十进制整数 |
%e | 浮点数,e记数法 |
%E | 浮点数,e记数法 |
%f | 浮点数,十进制记数法 |
%g | 根据值的不同,自动选择%f或%e。%e格式用于指数小于-4或者大于或等于精度时 |
%G | 根据值的不同,自动选择%f或%E。%E格式用于指数小于-4或者大于或等于精度时 |
%i | 有符号十进制整数(与%d相同) |
%o | 无符号八进制整数 |
%p | 指针 |
%S | 字符串 |
%u | 无符号十进制整数 |
%x | 无符号十六进制整数,使用十六进制数0f |
%X | 无符号十六进制整数,使用十六进制数0F |
%% | 打印一个百分号 |
在%和转换字符之间插入修饰符可修饰基本的转换说明。
- 表1和表2列出可作为修饰符的合法字符如果要插入多个字符,其书写顺序应该与表1中列出的顺序相同。
表1 printf()的修饰符
修饰符 | 含义 |
---|---|
标记 | 表 2 描述了5种标记(+、-、空格、#和0),可以不使用标记或使用多个标记示例:“%-10d" |
数字 | 最小字段宽度:如果该字段不能容纳待打印的数字或字符串,系统会使用更宽的字段示例:"%4d" |
.数字 | 对于%e、%E和%f转换,表示小数点右边数字的位数 对于%g和%G转换,表示有效数字最大位数 对于%s转换,表示待打印字符的最大数量 对于整型转换,表示待打印数字的最小位数 如有必要,使用前导0来达到这个位数 只使用.表示其后跟随一个0,所以%.f和%.0f 相同 示例:"%5.2f"打印一个浮点数,字段宽度为5字符,其中小数点后有两位数字 |
h | 和整型转换说明一起使用,表示short int或unsigned short int 类型的值 示例:"%hu"、"%hx"、”%6.4hd" |
hh | 和整型转换说明一起使用,表示signed char或unsigned char类型的值 示例:"%hhu"、"%hhx"、"%6.4hhd" |
j | 和整型转换说明一起使用,表示intmax_t或uintmax_t类型的值。这些类型定义在stdint.h中 示例:"%jd"、"%8jx" |
l | 和整型转换说明一起使用,表示long int或unsigned long int 类型的值 示例:"%ld"、"%8lu" |
ll | 和整型转换说明一起使用,表示long long int或unsigned long long int 类型的值(C99) 示例:"%lld”、“%8llu" |
L | 和浮点转换说明一起使用,表示long double 类型的值 示例:"%Ld”、“%10.4Le" |
t | 和整型转换说明一起使用,表示ptrdiff_t类型的值。ptrdiff_t是两个指针差值的类型(C99) 示例:"%td”、"%12ti" |
z | 和整型转换说明一起使用,表示size_t类型的值。size_t是sizeof返回的类型(C99) 示例:"%zd""%12zd |
表2 printf()中的标记
标记 | 含义 |
---|---|
– | 待打印项左对齐。即,从字段的左侧开始打印该项 示例:"%-20s" |
+ | 有符号值若为正,则在值前面显示加号;若为负,则在值前面显示减号 示例:"%+6.2f" |
空格 | 有符号值若为正,则在值前面显示前导空格(不显示任何符号);若为负,则在值前面显示减号 +标记覆盖一个空格 示例:"%6.2f" |
h | 和整型转换说明一起使用,表示short int或unsigned short int 类型的值 示例:"%hu"、"%hx"、”%6.4hd" |
# | 把结果转换为另一种形式。如果是%o格式,则以0开始:如果是%x或%X格式,则以0x或 0X开始:对于所有的浮点格式,#保证了即使后面没有任何数字,也打印一个小数点字符。对于%g和%G格式,#防止结果后面的0被删除 示例:"%#o”、"%#8.0f”、"%+#10.3e" |
0 | 对于数值格式,用前导0代替空格填充字段宽度。对于整数格式,如果出现-标记或指定精度,则忽略该标记 |
sizeof 与 类型可移植性
- sizeof 运算符以字节为单位返回类型或值的大小。这应该是某种形式的整数,但是标准只规定了该值是无符号整数,在不同的实现中,它可以是unsignedint、unsigned long甚至是unsigned1ong long。
- 因此,如果要用 printf()函数显示 sizeof 表达式,根据不同系统,可能使用%u、%lu或%llu。这意味着要查找你当前系统的用法,如果把程序移植到不同的系统还要进行修改。
- 鉴于此,C提供了可移植性更好的类型。
- 首先,stddef.h头文件(在包含stdio.h头文件时已包含其中)把size_t定义成系统使用 sizeof返回的类型,这被称为底层类型(underlyingtype).
- 其次,printf()使用z修饰符表示打印相应的类型。
- 同样,C还定义了ptrdiff_t类型和t修饰符来表示系统使用的两个地址差值的底层有符号整数类型.
float 参数的转换
对于浮点类型,有用于 double 和 long double 类型的转换说明,却没有 float 类型的。这是因为在K&RC中,表达式或参数中的 float 类型值会被自动转换成 double 类型。一般而言,ANSIC不会把 float 自动转换成 double。然而,为保护大量假设 float类型的参数被自动转换成 double的现有程序,printf()函数中所有float 类型的参数(对未使用显式原型的所有C函数都有效)仍自动转换成 double 类型。因此,无论是K&RC还是ANSIC,都没有显示 float 类型值专用的转换说明。
打印宽度示例:
#include <stdio.h>
#define PAGES 959 // 长度为3
int main(void) {
printf("*%d*\n",PAGES);
printf("*%2d*\n",PAGES); // 实际长度超过2
printf("*%10d*\n",PAGES);// 实际长度不到10, 默认右对齐
printf("*%-10d*\n",PAGES);// 左对齐
return 0;
}
/*
*959*
*959*
* 959*
*959 *
*/
浮点数打印示例:
#include <stdio.h>
int main(void) {
const double PI = 3.14159265358979323846;
printf("*%f*\n", PI); //默认最多打印6位小数
printf("%e*\n", PI);
printf("*%4.2f*\n", PI);
printf("*%3.1f*\n", PI);
/*
*3.141593*
3.141593e+00*
*3.14*
*3.1*
*/
printf("********************************\n");
printf("*%10.3f*\n", PI); //宽度10
printf("*%-10.3f*\n", PI); //宽度10,左对齐
printf("*%-10.3e*\n", PI);
printf("*%-10.3E*\n", PI);
/*
* 3.142*
*3.142 *
*3.142e+00 *
*3.142E+00 *
*/
printf("********************************\n");
printf("*%+4.3f*\n", PI); //正负号
printf("*%+4.3f*\n", -PI); //正负号
/*
*+3.142*
*-3.142*
*/
printf("********************************\n");
printf("*%010.3f*\n", PI);
printf("*%010.3e*\n", PI);
/*
*000003.142*
*03.142e+00*
*/
return 0;
}
另一个示例:
#include <stdio.h>
int main(void) {
printf("%x %X %#X\n",31,31,31); //大小写,前缀
printf("% d\n",4); //前导空格演示,正负号对齐
printf("% d\n",-4); //前导空格演示,正负号对齐
printf("*%5d*%5.3d*%05d*%05.3d*\n",6,6,6,6); //
return 0;
}
/*
1f 1F 0X1F
4
-4
* 6* 006*00006* 006*
*/
字符串示例:
#include <stdio.h>
#define HELLO "Hello World!"
int main(void) {
printf("%2s\n", HELLO); // 会突破2的长度限制
printf("%20s\n", HELLO); // 默认左对齐
printf("%20.2s\n", HELLO); // 不会突破2的长度限制
printf("%-20.2s\n", HELLO);//左对齐
return 0;
}
/*
Hello World!
Hello World!
He
He
*/
printf中的转换说明只是把原始的二进制值转换成指定的形式显示出来,不会改变原来的值。
#include <stdio.h>
#define PAGES 336
#define WORDS 65618
int main(void) {
short num =PAGES;
short num2=65;
printf("print num as short and unsigned short: %hd %hu \n",num,num);
printf("print -num as short and unsigned short: %hd %hu \n",-num,-num); // 试图用hu无符号格式打印负数
printf("print num as int and char: %d %c \n",num,num); // num 已经超过char的取值范围
printf("print num2 as int and char: %d %c \n",num2,num2); // OK
printf("print WORDS 65618 as int short,unsigned short , and char: %d %hd %hu %c \n",WORDS,WORDS,WORDS,WORDS);
return 0;
}
/*
print num as short and unsigned short: 336 336
print -num as short and unsigned short: -336 65200
print num as int and char: 336 P
print num2 as int and char: 65 A
print WORDS 65618 as int short and char: 65618 82 82 R
*/
printf 会依次对每个变量进行转换:
#include <stdio.h>
int main(void) {
float n1 = 3;
double n2 = 3;
long n3 = 2000000000;
long n4 = 1234567890;
printf("%.1e %.1e %.1e %.1e\n", n1, n2, n3, n4); // e只处理浮点数
// 3.0e+00 3.0e+00 9.9e-315 2.8e-312
printf("%ld %ld \n", n3, n4);
// 2000000000 1234567890
printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
// 0 0 2000000000 1234567890
return 0;
}
scanf()
-
scanf()把输入的字符串转换成整数、浮点数、字符或字符串
-
printf()正好与printf相反,把整数、浮点数、字符和字符串转换成显示在屏幕上的文本。
-
scanf()和printf()类似,也使用格式字符串和参数列表。scanf()中的格式字符串表明字符输入流的目标数据类型。
-
两个函数主要的区别在参数列表中。printf()函数使用变量、常量和表达式,而scanf(函数使用指向变量的指针。
-
如果用scanf()读取基本变量类型的值,在变量名前加上一个&;
-
如果用scanf()把字符串读入字符数组中,不要使用&。
-
#include <stdio.h>
int main(void) {
int age;
char name[40];
printf("请输入你的年龄和名字: \n");
scanf("%d",&age);
scanf("%s",name);
printf("%s,你的年龄为 %d\n", name,age);
return 0;
}
/*
请输入你的年龄和名字:
18
Zhang San
Zhang,你的年龄为 18
*/
如果两个变量输入放一条sanf语句中,用空格隔开
#include <stdio.h>
int main(void) {
int age;
char name[40];
printf("请输入你的年龄和名字: \n");
scanf("%d %[^\n]",&age,name);
// scanf("%s",name);
printf("%s,你的年龄为 %d\n", name,age);
return 0;
}
/*
请输入你的年龄和名字:
18 Zhang San
Zhang San,你的年龄为 18
*/
scanf()函数所用的转换说明与printf()函数几乎相同。主要的区别是,对于float类型和 double类型,printf()都使用%f、%e、%E、%g和%G转换说明。而 scanf()只把它们用于 float 类型,对于double 类型时要使用字母l修饰符。C99标准中常用的转换说明:
转换说明 | 含义 |
---|---|
%c | 把输入解释成字符 |
%d | 把输入解释成有符号十进制整数 |
%e、%f、%g、%a | 把输入解释成浮点数(C99标准新增了%a) |
%E、%F、%G、%A | 把输入解释成浮点数(C99标准新增了%A) |
%i | 把输入解释成有符号十进制整数 |
%o | 把输入解释成有符号八进制整数 |
%p | 把输入解释成指针(地址) |
%s | 把输入解释成字符串。从第1个非空白字符开始,到下一个空白字符之前的所有字符都是输入 |
%u | 把输入解释成无符号十进制整数 |
%x、%X | 把输入解释成有符号十六进制整数 |
可以在上表所列的转换说明中(百分号和转换字符之间)使用修饰符。如果要使用多个修饰符,必须按下表所列的顺序书写。
转换说明 | 含义 |
---|---|
* | 抑制赋值 示例:""%*d"" |
数字 | 最大字段宽度。输入达到最大字段宽度处,或第1次遇到空白字符时停止 示例:""%10s"" |
hh | 把整数作为signed char 或unsigned char 类型读取 示例:"%hhd"、"%hhu" |
ll | 把整数作为long long或unsigned long long类型读取(C99) 示例:"%lld"、"%llu" |
h、l或L | "%hd"和"%hi"表明把对应的值储存为short int类型 "%ho"、"%hx"和"%hu"表明把对应的值储存为unsigned shot int类型 "%ld"和"%li"表明把对应的值储存为long类型 "%lo"、"%lx"和"%lu"表明把对应的值储存为unsigned long类型 "%le"、"%lf"和"%lg"表明把对应的值储存为double类型 在e、f和g前面使用L而不是L,表明把对应的值被储存为long double类型。 如果没有修饰符,d、i、o和x表明对应的值被储存为int类型,f和g表明把对应的值储存为float类型 |
j | 在整型转换说明后面时,表明使用intmax_t或uintmax_t类型(C99) 示例:"%zd"、"%zo" |
z | 在整型转换说明后面时,表明使用sizeof的返回类型(C99) |
t | 在整型转换说明后面时,表明使用表示两个指针差值的类型(C99) 示例:"%td"、"%tx" |
如上表所示,使用转换说明比较复杂,而且这些表中还省略了一些特性。省略的主要特性是,从高度格式化源中读取选定数据,如穿孔卡或其他数据记录。因为在本书中,scanf()主要作为与程序交互的便利工具,所以这里没有讨论更复杂的特性。
从 scanf()角度看输入
-
假设 scanf()根据一个%d 转换说明读取一个整数。scanf()函数每次读取一个字符,跳过所有的空白字符,直至遇到第1个非空白字符才开始读取。因为要读取整数,所以 scanf()希望发现一个数字字符或者一个符号(+或-)。如果找到一个数字或符号,它便保存该字符,并读取下一个字符。如果下一个字符是数字,它便保存该数字并读取下一个字符。scanf()不断地读取和保存字符,直至遇到非数字字符。如果遇到一个非数字字符,它便认为读到了整数的末尾。然后,scanf()把非数字字符放回输入。这意味着程序在下一次读取输入时,首先读到的是上一次读取丢弃的非数字字符。最后,scanf()计算已读取数字(可能还有符号)相应的数值,并将计算后的值放入指定的变量中。
-
如果使用字段宽度,scanf()会在字段结尾或第1个空白字符处停止读取(满足两个条件之一便停止)。如果第1个非空白字符是A而不是数字,scanf()将停在那里,并把A放回输入中不会把值赋给指定变量。程序在下一次读取输入时,首先读到的字符是A。如果程序只使用%d转换说明,scanf()就一直无法越过A读下一个字符。
-
如果使用带多个转换说明的 scanf(),C规定在第1个出错处停止读取输入。
-
用其他数值匹配的转换说明读取输入和用%d的情况相同。区别在于scanf()会把更多字符识别成数字的一部分。例如,x转换说明要求 scanf()识别十六进制数 a~f和 A~E。浮点转换说明要求 scanf ()识别小数点、e记数法(指数记数法)和新增的p记数法(六进制指数记数法)。
-
如果使用%s转换说明,scanf()会读取除空白以外的所有字符。scanf()跳过空白开始读取第1个非空白字符,并保存非空白字符直到再次遇到空白。这意味着scanf()根据%s 转换说明读取一个单词,即不包含空白字符的字符串。如果使用字段宽度,scanf()在字段末尾或第1个空白字符处停止读取。无法利用字段宽度让只有一个%s的 scanf()读取多个单词。
-
当scanf()把字符串放进指定数组中时,它会在字符序列的末尾加上’\0’,让数组中的内容成为一个C字符串。
-
在C语言中scanf(并不是最常用的输入函数。这里重点介绍它是因为它能读取不同类型的数据。C语言还有其他的输入函数,如getchar()和fgets()。这两个函数更适合处理一些特殊情况,如读取单个字符或包含空格的字符串。
格式字符串中的普通字符
scanf()函数允许把普通字符放在格式字符串中。除空格字符外的普通字符必须与输入字符串严格匹配,例如,假设在两个转换说明中添加一个逗号:
scanf("%d,%d",&n,&m);
scanf()函数将其解释成:用户将输入一个数字、一个逗号,然后再输入一个数字。也就是说,用户必须像下面这样进行输入两个整数:
88, 121
由于格式字符串中,%d后面紧跟逗号,所以必须在输入88后再输入一个逗号。但是,由于scanf()会跳过整数前面的空白,所以下面两种输入方式都可以:
88,121
和
88,
121
-
除了号c,其他转换说明都会自动跳过待输入值前面所有的空白。因此,scanf("%d%d",&n,&m)与 scanf("%d %d",&n,&m)的行为相同。
-
对于c,在格式字符串中添加一个空格字符会有所不同。例如,如果把%c 放在格式字符串中的空格前面,scanf()便会跳过空格,从第1个非空白字符开始读取。也就是说,scanf("%c",&ch)从输入中的第1个字符开始读取,而scanf( ” %c",&ch)则从第1个非空白字符开始读取,输入中第一个并非必须是空字符。
第一种情况:
#include <stdio.h>
int main(void) {
char c_first;
scanf(" %c", &c_first);
printf("%c\n", c_first);
return 0;
}
/*
a bcd
a
*/
第二种情况:
#include <stdio.h>
int main(void) {
char c_first;
scanf(" %c", &c_first);
printf("%c\n", c_first);
return 0;
}
/*
abc
a
*/
scanf()的返回值
scanf()函数返回成功读取的项数。如果没有读取任何项,且需要读取一个数字而用户却输入一个非数值字符串,scanf()便返回 0。当scanf()检测到“文件结尾”时,会返回EOF(EOF是stdio.h中定义的特殊值,通常用#define 指令把 EOF 定义为-1)。
#include <stdio.h>
int main(void) {
int i,j,k,r;
printf("输入三个整数:i,j,k\n ");
r = scanf("%d,%d,%d",&i,&j,&k);
printf("输入:%d %d %d\n",i,j,k);
printf("scanf的返回值:%d\n",r);
return 0;
}
/*
输入三个整数:i,j,k
2,3,4
输入:2 3 4
scanf的返回值:3
*/
printf()和scanf()的*
修饰符
printf()和 scanf()都可以使用*修饰符来修改转换说明的含义。但是,它们的用法不太一样。
- printf()中如果不想预先指定字段宽度,希望通过程序来指定,那么可以用
*
修饰符代替字段宽度。但还是要用个参数告诉函数,字段宽度应该是多少。也就是说,如果转换说明是*d
,那么参数列表中应包含*
和d
对应的值。这个技巧也可用于浮点值指定精度和字段宽度。 - scanf()中
*
的用法与此不同。把*
放在和转换字符之间时,会使得scanf()跳过相应的输出项。在程序需要读取文件中特定列的内容时,这项跳过功能很有用。
#include <stdio.h>
int main(void) {
int width = 10, precision = 3;
int number = 1234;
double pi = 3.141592653589793;
printf("%%d:%d \n", number);
printf("%%*d:%*d \n", width, number);
printf("%%f:%f \n", pi);
printf("%%*.*f:%*.*f\n", width, precision, pi);
int i = 0, j = 0, k = 0;
printf("请输入3个整数 i j k\n");
scanf("%*d %*d %d",&k);
printf("i=%d, j=%d, k=%d\n", i, j, k);
return 0;
}
/*
%d:1234
%*d: 1234
%f:3.141593
%*.*f: 3.142
请输入3个整数 i j k
10 11 12
i=0, j=0, k=12
*/
C语言用char 类型表示单个字符,用字符串表示字符序列。字符常量是一种字符串形式,即用双引号把字符括起来:"Good luck,my friend"。可以把字符串储存在字符数组(由内存中相邻的字节组成)中。字符串,无论是表示成字符常量还是储存在字符数组中,都以一个叫做空字符\0
的隐藏字符结尾。