2024年10月21日 16:56

This commit is contained in:
许大仙 2024-10-21 08:56:58 +00:00
parent 7fe9879a29
commit 0de702ce94
2 changed files with 416 additions and 0 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -251,12 +251,424 @@ int main() {
## 2.1 概述
* printf 函数的核心作用就是将各种数据类型的数据转换为字符的形式输出到 `stdout` 缓冲区中。
* 语法:
```c
extern int printf (const char *format, ...);
```
> [!NOTE]
>
> * ① format 参数是`格式化字符串`,常见的格式占位符有 `%d`、`%f` 等。
> * ② printf 函数和 scanf 函数只需要大致了解一下用法,不比深究。
> * ③ 在实际开发中,如果我们使用 Qt 开发,或使用 C++ 作为服务器开发,会有更高级的输入输出功能,如:使用 C++ 的标准输出流 `std::cout``std::cin`,或者直接使用日志库(如:`spdlog`、`glog` 等)来处理日志和调试输出。
* printf 函数的语法规则,如下所示:
![](./assets/11.svg)
> [!NOTE]
>
> * ① 对于 format 参数中的非格式化字符串普通字符printf 函数会将其作为普通字符原封不动的进行显示,如:`我今年 岁`。
> * ② 对于 format 参数中的格式化字符串,即:以 `%`开头的字符,会和后面输出列表中的字符一一匹配,然后将匹配到的字符替换对应的格式化字符,如:`我今年%d岁`中的`%d`会被替换为`18` 。
* 示例:
```c
#include <stdio.h>
int main() {
// 禁用 stdout 缓冲区
setbuf(stdout, nullptr);
// 声明变量并赋值
int num = 18;
// 使用输出语句,将变量 num 的值输出,其中 %d 表示输出的是整数
printf("我今年%d岁\n", num);
return 0;
}
```
## 2.2 格式占位符的转换说明
* 语法:
```c
%[标志][字段宽度][.精度][长度]说明符
```
> [!IMPORTANT]
>
> * ① `%` 是`格式占位符`的`开头`,是必不可少的,其余部分可以省略。
> * ② `说明符`是`格式占位符`的`结尾`,是必不可少的,其余部分可以省略。
>
> | 格式符 | 说明 |
> | ---------- | ------------------------------------------------------------ |
> | `d``i` | 表示有符号的十进制整数。 |
> | `u` | 表示无符号的十进制整数。 |
> | `o` | 表示无符号的八进制整数。 |
> | `x` | 表示无符号的十六进制整数,使用小写字母(例如:`a-f`)。 |
> | `X` | 表示无符号的十六进制整数,使用大写字母(例如:`A-F`)。 |
> | `f` | 浮点数(普通浮点数表示) |
> | `e` | 强制用科学计数法显示此浮点数使用小写的“e”表示10的幂次。 |
> | `E` | 强制用科学计数法显示此浮点数使用大写的“E”表示10的幂次。 |
> | `g` | 选择最合适的表示方式,浮点数或科学记数法。<br>当选择使用科学计数法显示此浮点数时使用小写的“e”表示10的幂次。 |
> | `G` | 选择最合适的表示方式,浮点数或科学记数法。<br/>当选择使用科学计数法显示此浮点数时使用大写的“E”表示10的幂次。 |
> | `c` | 字符 |
> | `s` | 字符串 |
> | `p` | 指针 |
> [!NOTE]
>
> * ① `[标志]`用于决定一些特殊的格式,如:
> * `-`:左对齐输出。如果没有该标志,默认是右对齐输出。
> * `+`:输出正负号。对于正数,会输出 `+`;对于负数,会输出 `-`
>
> * ② `[字段宽度]`用于指定输出的最小字符宽度,但不会导致截断数据:
> * 如果输出的字符,宽度小于指定的宽度,那么输出的值将会按照指定的**`[标志]`**来进行填充。若标志位没有 0 ,则会填充空格。
> * 如果输出的字符,宽度大于指定的宽度,那么 printf 函数并不会截断,而是完全输出所有字符。
> * ③ `[.精度]`定义打印的精度:
> * 对于整数,表示要输出的最小位数,若位数不足则左侧填充 0 。
> * 对于浮点数,表示要在小数点后面打印的位数。
> * 当有效数字不足时,会自行在后面补 0 。
> * 当有效位数超出时,会截断保留指定的有效位数。这个过程一般会遵守 "四舍五入" 的原则。
> * 但由于浮点数存储的固有精度问题,某些数值可能不能完美表示,导致结果中的数字稍有偏差。
> * 需要注意的是,在不指定`[.精度]`的情况下,浮点数默认显示 6 位小数,多的部分舍弃,不够的话,会在后面补 0 。
> * ④ `[长度]`主要描述参数的数据类型或大小。常见的长度修饰符有:
>
> | 长度修饰符 | 说明 |
> | ------------------ | ------------------------------------------------------------ |
> | `h` | 与整数说明符一起使用,表示 short 类型。 |
> | `l (小写的 L)` | 通常与整数或浮点数说明符一起使用,表示 long对于整数或 double对于浮点数。 |
> | `ll (两个小写的L)` | 与整数说明符一起使用,表示 long long 类型的整数。 |
> | `L (大写的L)` | 与浮点数说明符一起使用,表示 long double 。 |
* 示例:
```c
#include <stdio.h>
int main() {
// 禁用 stdout 缓冲区
setbuf(stdout, nullptr);
printf("|%4f|\n", 3.14159f);
printf("|%10f|\n", 3.14159f);
printf("|%.4f|\n", 3.14159f);
printf("|%4.1f|\n", 3.14159f);
printf("|%04.1f|\n", 3.14159f);
printf("|% 4.1f|\n", 3.14159f);
printf("|%-4.1f|\n", 3.14159f);
printf("|%+4.1f|\n", 3.14159f);
return 0;
}
```
* 示例:
```c
#include <stdio.h>
int main() {
// 禁用 stdout 缓冲区
setbuf(stdout, nullptr);
int i = 40;
float x = 839.21f;
printf("|%d|%5d|%-5d|%5.3d|\n", i, i, i, i);
printf("|%f|%10f|%10.2f|%-10.2f|\n", x, x, x, x);
return 0;
}
```
## 2.3 格式占位符中的特殊符号 %
* 在格式占位符中 `%`用于表示转换的开头。如果我们也希望打印一个 `%`,就可以使用 `%%` 来表示一个 `%`
* 示例:
```c {11}
#include <stdio.h>
int main() {
// 禁用 stdout 缓冲区
setbuf(stdout, nullptr);
int progress = 50;
// 下载进度: 50%
printf("下载进度: %d%%\n", progress);
return 0;
}
```
## 2.4 格式占位符中的特殊符号 *
* 如果我们希望变量在程序运行期间能够打印小数点后的位置以及打印结果的总宽度,就可以在格式占位符中通过 * 来代替。
* 示例:
```c {11}
#include <stdio.h>
int main() {
// 禁用 stdout 缓冲区
setbuf(stdout, nullptr);
int width = 5;
int point = 2;
printf("|%*.*f|", width, point, 3.1415); // | 3.14|
return 0;
}
```
## 2.5 格式占位符中的 %f 和 %lf
* 格式占位符 `%f` 是用来输出 `float` 类型的数据的,而 格式占位符 `%lf` 是用来输出 `double` 类型的数据的。
> [!IMPORTANT]
>
> * ① `%f``%lf` 是完全等价的。
> * ② 在 C99 之后的标准中,当使用 printf 函数打印浮点数的时候,不管是 float 还是 double 都会自动提升到 double 来进行处理。
> * ③ 仅限于 printf 函数scanf 函数没有这样的特点scanf 函数中的 %f 和 %lf 是不一样的。
* 示例:
```c {10-11}
#include <stdio.h>
int main() {
// 禁用 stdout 缓冲区
setbuf(stdout, nullptr);
double num = 123.456;
printf("使用%%f打印的结果是: %f\n", num);
printf("使用%%lf打印的结果是: %lf\n", num);
return 0;
}
```
## 2.6 printf 函数中的返回值
* 对于 printf 函数其实是有返回值的,如下所示:
```c
extern int printf (const char *format, ...);
```
> [!NOTE]
>
> * ① 如果输出成功,将返回函数实际输出的字符总数。并且当输出成功时,返回值是一个非负数。
> * ② 如果输出失败,返回值就是一个负数。
> * ③ 在实际开发中printf 函数的返回值比较少被接受处理。
* 示例:
```c {8,11}
#include <stdio.h>
int main() {
// 禁用 stdout 缓冲区
setbuf(stdout, nullptr);
int ret = printf("hello\n");
printf("ret = %d\n", ret); // 正常输出了6个字符所以返回值是6
int ret2 = printf("");
printf("ret2 = %d\n", ret2); // 正常输出了0个字符所以返回值是0
return 0;
}
```
## 2.7 行缓冲注意事项
* printf 函数将数据输出到 stdout 的行缓冲区,但要将这些数据真正展示到外部设备(如屏幕),则需依靠 stdout 的自动刷新机制。
> [!NOTE]
>
> 为了增加输出的实时性和可预测性,有如下的常见策略:
>
> * ① 输出字符串的末尾添加换行符 `"\n"` ,这样可以立即触发缓冲区的刷新。
> * ② 使用 setbuf 函数禁用 stdout 的行缓冲区。
> * ③ 使用 fflush 函数手动刷新 stdout 的行缓冲区。
> * ④ ...
>
> 本人选择的是第 ② 种方案;但是,如果你选择第 ① 种方案,那么应该在不影响程序逻辑的前提下。
* 示例:
```c
#include <stdio.h>
int main() {
// 禁用 stdout 缓冲区
setbuf(stdout, nullptr);
int chinese, math, english;
float average;
printf("请输入语文成绩:");
scanf("%d", &chinese);
printf("请输入数学成绩:");
scanf("%d", &math);
printf("请输入英语成绩:");
scanf("%d", &english);
average = (chinese + math + english) / 3.0;
printf("平均成绩为:%.2f\n", average);
return 0;
}
```
# 第三章scanf 函数
## 3.1 概述
* scanf 函数的核心作用就是从 `stdin 缓冲区`读取字符形式的数据,并将其转换为特定类型的数据。
* 语法:
```c
extern int scanf (const char *__restrict __format, ...)
```
> [!NOTE]
>
> * ① scanf 函数和 printf 函数最大的不同就是,在参数列表中中的参数是变量的地址,即:将读取到的值存放在哪个地址。
> * ② 也可以认为scanf 函数的格式是:`scanf(格式化字符串, &变量1, &变量2, ...);`,但是变量前面的 `&` 在某些情况下是可以省略的。
> * ③ 对于 scanf 函数中的格式化字符串,除了格式占位符之外,通常不需要普通字符。
* 示例:
```c
#include <stdio.h>
int main() {
// 禁用 stdout 缓冲区
setbuf(stdout, nullptr);
int chinese, math, english;
float average;
printf("请输入语文成绩:");
scanf("%d", &chinese);
printf("请输入数学成绩:");
scanf("%d", &math);
printf("请输入英语成绩:");
scanf("%d", &english);
average = (chinese + math + english) / 3.0;
printf("平均成绩为:%.2f\n", average);
return 0;
}
```
## 3.2 格式占位符的转换说明
* 语法:
```c
%[*][字段宽度][长度]说明符
```
> [!IMPORTANT]
>
> * ① `%` 是`格式占位符`的`开头`,是必不可少的,其余部分可以省略。
> * ② `说明符`是`格式占位符`的`结尾`,是必不可少的,其余部分可以省略。
>
> | 格式符 | 说明 |
> | ------------ | ------------------------------------------------------------ |
> | `d` | 表示有符号的十进制整数。 |
> | `i` | `scanf` 的 i 会自动判断输入的整数的进制,支持八进制、十进制和十六进制。<br>`scanf` 中的 i 和 printf 中的 i 不一样。 |
> | `u` | 表示无符号的十进制整数。 |
> | `o` | 表示无符号的八进制整数。 |
> | `x` | 表示无符号的十六进制整数,使用小写字母(例如:`a-f`)。 |
> | `X` | 表示无符号的十六进制整数,使用大写字母(例如:`A-F`)。 |
> | `f` | 浮点数(普通浮点数表示) |
> | `e` | 强制用科学计数法显示此浮点数使用小写的“e”表示10的幂次。 |
> | `E` | 强制用科学计数法显示此浮点数使用大写的“E”表示10的幂次。 |
> | `g` | 选择最合适的表示方式,浮点数或科学记数法。<br>当选择使用科学计数法显示此浮点数时使用小写的“e”表示10的幂次。 |
> | `G` | 选择最合适的表示方式,浮点数或科学记数法。<br/>当选择使用科学计数法显示此浮点数时使用大写的“E”表示10的幂次。 |
> | `c` | 字符 |
> | `s` | 字符串 |
> | `p` | 指针 |
> | `%[字符集]` | 告诉`scanf`只接受和存储来自指定字符集的字符。<br>例如:`%[abc]`将只读取 'a'、'b' 或 'c'字符,其他的字符将导致读取停止。 |
> | `%[^字符集]` | 这是扫描集的否定形式,告诉`scanf`接受和存储除了指定字符集之外的所有字符。<br>例如:`%[^abc]`将读取除了'a'、'b', 和 'c'之外的所有字符,直到遇到这三个字符中的任何一个为止。 |
* 示例:
```c
#include <stdio.h>
int main() {
// 禁用 stdout 缓冲区
setbuf(stdout, nullptr);
int chinese, math, english;
float average;
printf("请输入语文成绩:");
scanf("%d", &chinese);
printf("请输入数学成绩:");
scanf("%d", &math);
printf("请输入英语成绩:");
scanf("%d", &english);
average = (chinese + math + english) / 3.0;
printf("平均成绩为:%.2f\n", average);
return 0;
}
```