【C-Primer-Plus读书笔记】第4章:字符串和格式化输入/输出
字符串简介
字符串是一个或多个字符的序列。
- char类型数组和null字符
字符串都被存储在char类型的数组中,数组末尾位置有一个字符\0
,这是空(null)字符,C语言用它标记字符串的结束。C中的字符串一定是以空字符结束的,这意味着数组的容量必至少比待存储字符串中的字符多1。 使用字符串
- printf():使用
%s
打印一个字符串。 - scanf():使用
%s
读取一个字符串,你不用亲自把空字符放入字符串末尾,scanf()在读取输入时就已经完成这项工作,字符串常量同样。注意,scanf()的%s
遇到第一个空白(空格、制表符或换行符)时就不再读取输入。 字符串和字符:字符串常量"x"和字符常量'x'不同。区别在于:
- 'x'是基本类型(char),而"x"是派生类型(char数组);
- "x"实际上由两个字符组成:'x'和空字符
\0
- printf():使用
strlen()函数
sizeof运算符以字节为单位给出对象大小,strlen()函数给出字符串中的字符长度。
虽然一字节存储一个字符,但sizeof和strlen()得出的结果并不相同,它们的主要区别是:
sizeof侧重该字符串所占用的存储空间,即即使字符串并没有占用满该char数组的空间,sizeof也给出所有空间大小;
strlen()则侧重字符串的实际长度,即字符串没有占用满char数组的空间,只给出实际字符串的长度。
常量和C预处理器
#define
(宏定义):符号常量(明示常量)。C预处理器会替换所有符号常量为其定义的实际值,这叫编译时替换。预处理器只做替换,不做任何计算,所以不用把定义符号常量当成变量定义,比如不用加赋值符号,不用分号结尾,实际上只是做一个对应关系即可。另外,符号常量通常采用全大写字母命名。
const
(C90标准):只读变量。用于限定一个变量为只读。与#define
最大区别是,const
是变量,会真实的把值存在内存里,处于程序运行时;而#define
只是一个符号对应定义,不存在内存,只完成替换,处于编译时替换。明示常量:C提供了一些定义好明示常量的头文件。如
limits.h
、float.h
,分别提供了与整型类型和浮点类型大小限制相关的详细信息。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 unsigned 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 类型的最大值 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类型值之间的差值
printf()和scanf()
printf()函数和scanf()函数能让用户可以与程序交流,它们是输入/输出函数,或简称I/O函数
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 |
%% | 打印一个百分号 |
使用printf()
printf()函数的格式:printf(格式字符串, 待打印项1, 待打印项2, ...);
格式字符串中的转换说明一定要与后面的每个项相匹配,若忘记这个基本要求会导致严重后果。
printf()的转换说明修饰符
修饰符 | 含义 |
---|---|
标记 | 有5种标记(-、+、空格、#和0),可以不使用标记或使用多个标记(参见下表) |
数字 | 最小字段宽度。如果该字段不能容纳待打印的数字或字符串,系统会使用更宽的字段 |
.数字 | 精度。对于%e 、%E 和%f 转换,表示小数点右边数字的位数;对于%g 和%G 转换,表示有效数字最大位数;对于%s 转换,表示待打印字符的最大数量;对于整型转换,表示待打印数字的最小位数;如有必要,使用前导0来达到这个位数;只使用. 表示其后跟随一个0,所以%.f 和%.0f 相同 |
h | 和整型转换说明一起使用,表示 short int 或 unsigned short int 类型的值 |
hh | 和整型转换说明一起使用,表示 signed char 或 unsigned char 类型的值 |
j | 和整型转换说明一起使用,表示 intmax_t 或 uintmax_t 类型的值。这些类型定义在 stdint.h 中 |
l | 和整型转换说明一起使用,表示 long int 或 unsigned long int 类型的值 |
ll | 和整型转换说明一起使用,表示 long long int 或 unsigned long long int 类型的值(C99) |
L | 和浮点转换说明一起使用,表示 long double 类型的值 |
t | 和整型转换说明一起使用,表示 ptrdiff_t 类型的值。ptrdiff_t是两个指针差值的类型(C99) |
z | 和整型转换说明一起使用,表示 size_t 类型的值。size_t 是 sizeof 返回类型(C99) |
- printf()中的标记:
标记 | 含义 |
---|---|
- | 待打印项左对齐 |
+ | 有符号值若为正,则在值前面显示加号;若为负,则在值前面显示减号 |
空格 | 有符号值若为正,则在值前面显示前导空格;若为负,则在值前面显示减号;+ 标记覆盖一个空格 |
# | 把结果转换为另外一种形式 |
0 | 对于数值格式,用前导0代替空格填充字段宽度。对于整数格式,如果出现- 标记或指定精度,则忽略该标记 |
转换说明的意义
转换说明把以二进制格式存储在计算机中的值转换成一系列字符(字符串)以便显示。转换说明是翻译说明
- 转换不匹配
转换说明应该与待打印值的类型相匹配。因为参数传递:函数传参,程序把传入的值放入被称为栈的内存区域,计算机根据变量类型(不是根据转换说明)把这些值放入栈中;printf()函数根据转换说明(不是根据变量类型)从栈中读取值,根据转换说明读取相应的字节数。 - printf()的返回值
返回打印字符的个数。如果有输出错误,则返回一个负值。 - 打印较长的字符串
参数间(逗号)可以断行,但不能在双引号括起来的字符串中间断行(按下Enter或Return键),应使用\n
表示换行字符。
使用scanf()
scanf()把输入的字符串转换成整数、浮点数、字符或字符串,而printf()正好相反,把整数、浮点数、字符和字符串转成显示在屏幕上的文本;两个函数的主要区别在于参数列表中,printf()函数使用变量、常量和表达式,而scanf()函数使用指向变量的指针。
scanf()函数使用空白(换行符、制表符和空格)把输入分成多个字段。再依次把转换说明和字段匹配时跳过空白;唯一例外的是%c
转换说明。根据%c
,scanf()会读取每个字符,包括空白。
- ANSI C中的scanf()的转换说明:
转换说明 | 含义 |
---|---|
%c | 把输入解释成字符 |
%d | 把输入解释成有符号十进制整数 |
%e、%f、%g、%a | 把输入解释成浮点数(C99标准新增了%a) |
%E、%F、%G、%A | 把输入解释成浮点数(C99标准新增了%A) |
%i | 把输入解释成有符号十进制整数 |
%o | 把输入解释成有符号八进制整数 |
%p | 把输入解释成指针(地址) |
%s | 把输入解释成字符串。从第一个空白字符开始,到下一个空白字符之前的所有字符都是输入 |
%u | 把输入解释成无符号十进制整数 |
%x、%X | 把输入解释成有符号十六进制整数 |
- scanf()转换说明中的修饰符:
转换说明 | 含义 |
---|---|
* | 抑制赋值 |
数字 | 最大字段宽度。输入达到最大字段宽度处,或第一次遇到空白字符时停止 |
hh | 把整数作为 signed char 或 unsigned char 类型读取 |
ll | 把整数作为 long long 或 unsigned long long 类型读取(C99) |
h、l或L | "%hd"和"%hi"表明把对应的值存储为short int类型; "%ho"、"%hx"和"%hu"表明把对应的值存储为unsigned short 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) |
z | 在整型转换说明后面时,表明使用 sizeof 的返回类型(C99) |
t | 在整型转换说明后面时,表明使用表示两个指针差值的类型(C99) |
从 scanf() 角度看输入
- scanf()函数每次读取一个字符,跳过所有的空白字符,直至遇到第一个非空白字符(除了
%c
转换说明)才开始读取。 - 遇到第一个空白字符(或其他不满足转换说明的不同类型数据)即读到了末尾,然后scanf()把剩下的字符放回输入。这意味着下次读取输入是从上一次丢弃的字符开始读取。
- 如果使用字段宽度,scanf()会在字段结尾或第一个空白字符停止读取(满足两个条件之一便停止)。
- 如果一直读取不了(转换说明不匹配)某个字符,scanf()就一直无法越过该字符读下一个字符。另外,如果使用带多个转换说明的scanf(),C规定在第一个出错处停止读取输入。
- scanf()函数每次读取一个字符,跳过所有的空白字符,直至遇到第一个非空白字符(除了
- 格式字符串中的普通字符
scanf()函数允许把普通字符放在格式字符串中。除空格字符外的普通字符必须与输入字符串严格匹配。 - scanf()的返回值
返回成功读取的项数。如没有读取到任何项,则返回0。当scanf()检测到文件结尾时,会返回EOF。
printf() 和 scanf() 的*
修饰符
如果你不想预先指定字段宽度,希望通过程序来指定,那么可以使用*
修饰符代替字段宽度。但还是需要一个参数告诉函数,字段宽度应是多少。
scanf()中的*
的用法有所不同。把*
放在%
和转换字符之间时,会使得scanf()跳过相应的输出项。