#include
int scanf(const char *format, ...);
参数:
format:字符串参数,包含三种类型。
int num1 = 0;
int num2 = 0;
// 此时 scanf 中有 7 个空白字符(空白字符不仅仅是空格,还可以是制表符、回车符)
// 但在实际输入中输入 1 个或多个空白字符都是可以正确读取的
// 至少要有一个空白字符是要分开两个数字,否则会被当成一个数字
scanf("%d %d", &num1, &num2);
// 同样的二进制数,不同的解释就会有不同的结果
// 8 位二进制数:1000 0001
// 有符号数:-1
// 无符号数:129
printf("%d\n", -1);
printf("%u\n", -1);
其他字符:当其他字符出现在格式字符串时,下一个输入字符必须与它匹配
如果匹配,该输入字符随后被丢弃
如果不匹配,函数就不再读取而是直接返回
int num1 = 0;
int num2 = 0;
// 如果在终端输入形式如下:12 31
// 那么此时 scanf 会直接返回,并不会将两个数字读取到 num1、num2
// 正确的输入形式应是:请输入第一个数字:12,请输入第二个数字:31
// 这样 scanf 才能正确读取
scanf("请输入第一个数字:%d,请输入第二个数字:%d", &num1, &num2);
...:表示一个可变长度的指针列表
scanf 参数前面为什么要加 & 符号就比较清楚了& 符号,将变量的值传递给函数,而 scanf 函数却把它解释为一个地址。当它被解引用将数据写入时,要么导致程序终止,要么导致一个不可预料的内存位置的数据被改写返回值:
当格式化字符串到达末尾或者读取的输入不再匹配格式字符所指定的类型时,输入就停止。
格式字符串的形式为:
%[可读取的最大宽度][限定符]类型
%[width][modifiers]type
width(宽度,可选)是一个数值,用于指定当前读取操作中读取的最大字符数。
int num = 0;
// 如果在终端输入 1002,num 最多只能读取三个字符,所以 num 的值为 100
scanf("%3d", &num);
| 格式代码 | h | l | L | ll |
|---|---|---|---|---|
| d、i、n | short | long | long long | |
| o、u、x | unsigned short | unsigned long | ||
| e、f、g | double | long double |
限定符的主要目的是为了指定参数的长度。如果整形参数比缺省的整形参数更短或更长,在格式代码中省略限定符就是一个常见的错误。浮点类型也是如此。
如果省略了限定符,可能会导致一个较长变量只有一部分被初始化,或者一个较短变量的相邻内存也被修改。
double dNum = 0.0;
long double ldNum = 0.0;
// 如果只使用 %f,那么就会导致有一部分没被初始化
scanf("%lf", &dNum);
scanf("%Lf", &ldNum);
short int sNum = 0;
// 如果只使用 %d,那么就会导致相邻内存被非法修改,这极有可能导致进程崩溃
scanf("%hd", &sNum);
C/C++ 并没有具体规定各种数据类型占用大小,只是给了一些限制,因此不同编译环境下结果可能不同,下表为 g++ 4.8.5 下的结果。
| 数据类型 | 占用内存(32位) | 占用内存(64位) |
|---|---|---|
| char | 1 | 1 |
| unsigned char | 1 | 1 |
| short | 2 | 2 |
| unsigned short | 2 | 2 |
| int | 4 | 4 |
| unsigned int | 4 | 4 |
| long | 4 | 8 |
| unsigned long | 4 | 8 |
| float | 4 | 4 |
| double | 8 | 8 |
| long double | 12 | 16 |
type(转换字符,必选)的内容及含义如下:
| 转换字符 | 参数类型 | 含义 |
|---|---|---|
| c | char* | 读取单个字符。如果给出宽度,就读取这个数目的字符 |
| i | int* | 根据前缀决定基数,可以是八进制(前缀 0)、十进制、十六进制(前缀 0x)有符号整数 |
| d | int* | 十进制有符号整数 |
| u、o、x | unsigned int* | 无符号整数。u 被解释为十进制、o 被解释为八进制、x 被解释为十六进制 |
| f、e、E、g、G | float* | 浮点型字面值常量。但小数点不是必须的,也可以使用指数形式(大小写没有区别) |
| s | char* | 读取一串非空字符串 |
| p | void* | 读取一个指针(即内存地址) |
| n | int* | 到目前为止通过调用 scanf 函数从输入读取的字符数被返回,%n 转换的字符并不在计算在 scanf 函数的返回值之内,它本身不消耗任何输出 |
| % | (无) | 两个 %% 就是 % 本身,与输入的 % 相匹配 |
int num = 0;
int num1 = 0;
int num2 = 0;
// 若在终端输入10 02
// 那么 num 的值为 7,因为中间输入的三个空格也是 scanf 读取到的字符
scanf("%d %d%n", &num1, &num2, &num);
printf("%d\n", num);
注意:当使用 %s 格式时,要小心缓冲区溢出的情况,最好在前面显式地控制读取的最大长度。
缓冲区溢出:10 个 char 大小的数组,却读取了 20 个字符,多的这部分就是溢出的,这就有可能超出当前函数栈帧引发段错误,或修改了不该修改的内存地址。
#include
int printf(const char *format, ...);
参数:
format:字符串参数,包含两种类型
...:用于转换的参数列表返回值:
如果函数调用成功,返回值是实际打印的字符数(不包含表示字符串结束的 ‘\0’);
如果函数调用失败,返回值是一个负数。
格式字符串的形式为:
%[标志][输出最小宽度][.精度][限定符]类型
%[flags][width][.precision][modifiers]type
flags 可同时出现多个,且无顺序要求。
| 标志 | 含义 |
|---|---|
| - | 指定被转换的参数在其字段内左对齐(默认是右对齐) |
| + | 指定在输出的数前面加上正负号 |
| 空格 | 如果第一个字符不是正负号,则在其前面加上一个空 |
| 0 | 对于数值转换,当输出长度小于字段宽度时,添加前导 0 进行填充 |
| # | 指定下表的另一种输出形式 |
| 用于 | #标志 |
|---|---|
| o | 保证产生的值以一个零开头 |
| x、X | 在非零值前加 0x 前缀(%X 则为 0X) |
| e、E、f | 确保结果始终包含一个小数点,即使后面没有数字 |
| g、G | 确保结果始终包含一个小数点,即使后面没有数字。另外,后缀的 0 并不会从小数中去除 |
width(宽度,可选)是一个数值,用于指定要输出的最小字符数。
转换后的参数输出宽度至少要达到这个数值。如果参数的字符数小于该数值,则在参数左边填充字符(默认是空格)。
-,要求参数左对齐的话则在右边填充.precision(.精度,可选),通过点号分隔字段的宽度和精度。
signed char/unsigned char 类型输出short/unsigned short 类型输出long/unsigned long 类型输出long long/unsigned long long 类型输出long double 类型输出
type(转换字符,必选)的内容及含义如下:
| 转换字符 | 参数类型 | 含义 |
|---|---|---|
| c | int | 参数被裁剪为 unsigned char 类型并作为字符打印 |
| d、i | int | 参数作为有符号十进制整数打印 |
| u、o、x、X | unsigned int | 参数作为无符号值打印。u 使用十进制;o 使用八进制;x 或 X 使用十六进制(两者的区别是:x 使用小写字母,X 使用大写字母) |
| e、E | double | 参数以指数形式输出单、双精度浮点数。小数点后面的位数由精度字段决定,默认是 6(两者的区别是:打印时 e 的大小写) |
| f | double | 参数按照常规的浮点数形式打印。小数点后面的位数由精度字段决定,默认是 6 |
| g、G | double | 参数以 %f 或 %e 中较短的输出宽度输出单、双精度浮点数 |
| s | char* | 打印一个字符串 |
| n | int* | 不产生任何输出,到目前为止函数所产生的输出字符数目将被保存到对应的参数中 |
| % | (无) | 打印一个 % 字符 |
注:如果 % 后边的字符不是转换字符,则其行为没有定义。