**核心思想:** % 格式控制符允许你精确控制数据的显示printf)或读取scanf)方式。尽管它们都使用 %,但其内部修饰符width, precision 等)的含义和行为有显著差异。

---

### **I. printf() 中的格式控制 %[flags][width][.precision]type)**

* **目的:** 控制数据在屏幕上的**格式化输出**。

#### **A. 基本结构**

```

% [flags] [width] [.precision] type

```

#### **B. type (转换类型)**

* 定义要输出的数据类型。

* **常见类型:**

* d, i: 有符号十进制整数

* u: 无符号十进制整数

* o: 八进制无符号整数

* x, X: 十六进制无符号整数 (小写/大写字母)

* f, F: 浮点数 (十进制表示,默认6位小数)

* e, E: 浮点数 (科学计数法)

* g, G: 浮点数 (根据值自动选择 fe)

* s: 字符串

* c: 字符

* p: 指针地址

* %: 输出一个字面 % 字符

#### **C. flags (可选,控制对齐、前缀、填充)**

* *-:左对齐**,默认是右对齐。

```c

printf("|%-10s|\n", "hello"); // Output: "|hello |"

```

* *+:强制在正数前显示 + 号**。

```c

printf("%+d\n", 123); // Output: "+123"

printf("%+d\n", -123); // Output: "-123"

```

* * (空格):在正数前显示空格** (如果 + 未使用)。

```c

printf("|% d|\n", 123); // Output: "| 123|"

printf("|% d|\n", -123); // Output: "|-123|"

```

* *0:用 0 填充**而不是空格 (仅用于数字类型,如果没指定 - 标志)。

```c

printf("|%05d|\n", 123); // Output: "|00123|"

```

* *#:替代形式** (例如%#x 会加上 0x 前缀%#o 0前缀%#f 即使小数部分为0也显示小数点)。

```c

printf("%#x\n", 255); // Output: "0xff"

printf("%#.0f\n", 3.0); // Output: "3."

```

#### **D. width (可选,整数):最小字段宽度**

* **位置:** 在 flags 之后,在 . 之前。

* **作用:** 指定输出的**最小字符数**。

如果数据本身的字符数*少于 width**:

默认*右对齐**,前面用**空格**填充。

* 如果使用 0 标志,则使用 0 填充。

如果使用 - 标志,则*左对齐**,后面用空格填充。

如果数据本身的字符数*多于 width**:

width 会被*忽略**,数据会**完全输出**,**不会截断**。

* **示例:**

* %7s: 最小7字符宽,右对齐,不足补空格。

```c

printf("|%7s|\n", "hi"); // Output: "| hi|"

printf("|%7s|\n", "topsecret"); // Output: "|topsecret|" (不截断)

```

* %5d: 最小5位数字宽,右对齐,不足补空格。

```c

printf("|%5d|\n", 12); // Output: "| 12|"

printf("|%05d|\n", 12); // Output: "|00012|"

```

#### **E. .precision (可选,以 . 开头后接整数):精度**

* **位置:** 在 width 之后。

* **作用:** 含义**取决于 type**。

* **具体类型说明:**

* **对于整数类型 d, i, u, o, x, X):**

表示输出的*最小数字位数**。

* 如果实际数字位数少于精度,则在前面补 0 填充。

* **不会截断**。

* **示例 %.30d:** 打印一个整数,使其至少有30位字符,不足则前面补 0

```c

printf("|%.5d|\n", 123); // Output: "|00123|"

printf("|%.30d|\n", 456); // Output: "|000000000000000000000000000456|"

printf("|%.2d|\n", 7); // Output: "|07|"

printf("|%.2d|\n", 12345); // Output: "|12345|" (多于精度不截断)

```

* **对于浮点类型 f, e, g):**

表示小数点后显示的*位数**。默认是6位。

* **会进行四舍五入。**

* **示例 %.2f:** 打印浮点数,保留两位小数。

```c

printf("|%.2f|\n", 3.14159); // Output: "|3.14|"

printf("|%.0f|\n", 6.789); // Output: "|7|"

```

* **对于字符串类型 s):**

表示输出的*最大字符数**。

如果字符串长度超过精度,则会*截断**。

* **示例 %7.3s:** 最小7字符宽,字符串最多取前3个字符,右对齐。

```c

printf("|%.3s|\n", "hello"); // Output: "|hel|"

printf("|%7.3s|\n", "hello"); // Output: "| hel|"

printf("|%7.7s|\n", "hi"); // Output: "| hi|" (少于精度不填充,只限制最大长度)

```

---

### **II. scanf() 中的格式控制 %[*][width][modifiers]type)**

* **目的:** 控制从输入流中**读取**数据的方式。

#### **A. 基本结构**

```

% [*] [width] [modifiers] type

```

#### **B. type (转换类型)**

* 定义要读取的数据类型。与 printf 类似,但也有一些 scanf 独有的:

* %d, %i, %u, %o, %x

* %f (读取 float), %lf (读取 double), %Lf (读取 long double)

* %s: 字符串 (读取非空白字符,遇到空白符停止)

* %c: 字符 (读取任何字符,包括空白符)

* %[chars]: 匹配指定字符集中的字符。

* %[^chars]: 匹配不在指定字符集中的字符。

#### **C. * (可选):抑制赋值**

* **位置:** 紧随 % 之后。

* **作用:** scanf 会读取匹配的输入项,但**不将其存储到对应的变量中**,而是直接丢弃。

* **示例:** scanf("%*d %s", str); 会读取并丢弃一个整数,然后读取一个字符串到 str

#### **D. width (可选,整数):最大字段宽度**

* **位置:** 在 %* 之后。

* **作用:** 指定从输入中读取的**最大字符数**。

* scanf 会读取最多 width 个字符,或者直到遇到不匹配的字符(对于数字)或空白字符(对于 %s),两者取其先。

如果输入数据超过 width,则*会截断**,只读取前 width 个字符。

* **示例:**

* %7s: 从输入中读取最多7个非空白字符到字符串变量。

```c

char buffer[20];

printf("Enter string: "); // Input: "HelloWorldExample"

scanf("%7s", buffer); // buffer will contain "HelloWo"

printf("Read: %s\n", buffer); // Output: "Read: HelloWo"

```

* %3d: 从输入中读取最多3位数字到整数变量。

```c

int num;

printf("Enter number: "); // Input: "123456"

scanf("%3d", &num); // num will be 123

printf("Read: %d\n", num); // Output: "Read: 123"

```

* %2c: 读取2个字符,包括空白字符。

```c

char c1, c2;

printf("Enter two chars: "); // Input: "a B" (a, space, B)

scanf("%2c", &c1); // c1 will be 'a', c2 will be ' '

scanf("%c", &c2);

printf("%c%c\n", c1, c2); // Output: "a " (读取了"a ",第二个scanf再读取"B")

```

* **注意scanf 中的 . (精度) 是无效的。** scanf 格式字符串中没有 .precision 这一概念。如果你尝试使用,它通常会被忽略或导致编译器警告。

#### **E. modifiers (可选,修饰符)**

* *h:** short intunsigned short int。 (如 %hd, %hu)

* *l:** long intunsigned long int %ld, %lu)double %lf).

* *ll:** long long intunsigned long long int %lld, %llu).

* *L:** long double %Lf).

---

### **III. 总结对比 printf vs. scanf)**

| 特性 | printf (输出) | scanf (输入) |

| :------------- | :-------------------------------------------------- | :-------------------------------------------------------- |

| *width** | **最小**字符数:不足补空/零,**超出不截断**。 | **最大**读取字符数:达到宽度或遇分隔符即停止,**超出截断**。 |

| *.precision** | **有意义!** 含义根据类型而变:<br>- s: **最大**字符数 (截断)<br>- f: 小数点后位数 (四舍五入)<br>- d, i: 最少数字位数 (补零) | **无意义!** 在 scanf 格式字符串中被忽略或导致警告。 |

| *flags** | 如 - (左对齐), 0 (补零), + (显示正号), # 等。 | **无**此概念,但有 * 标志表示抑制赋值。 |

| **目标** | 控制数据**如何显示**。 | 控制数据**如何读取**。 |

---

**关键记忆点:**

* *printfwidth 是“至少”precision 有时是“至多”s)或“至少”d)或“小数位”f)。**

* *scanfwidth 总是“至多”scanf 没有 precision。**

* *printf (out)** 偏向**美观和完整性**,通常不会轻易截断原始数据(除了字符串的 .precision)。

* *scanf (in)** 偏向**限制和解析**,会根据 width 截断输入数据。

理解这些差异对于正确编写 C 语言的输入输出代码至关重要。