---
# System prepended metadata

title: C Language / Fromatted Input & Output
tags: [C Language]

---

# C Language / Fromatted Input & Output

## 概述

C 語言中，所有輸入和輸出操作都是透過串流（stream） 進行，串流是一序列的位元組（bytes）。在輸入操作中，位元組從裝置（如鍵盤、磁碟機、網路連線等）流向主記憶體。在輸出操作中，位元組從主記憶體流向裝置（如顯示器螢幕、印表機、磁碟機、網路連線等）。

當程式執行開始時，會自動連接三個串流：

- 標準輸入串流連接到鍵盤
- 標準輸出串流連接到螢幕
- 第三個串流（標準錯誤串流）也連接到螢幕

好其實這章節就是在說 scanf 和 printf 函數的格式化功能，然後要包含標頭檔`<stdio.h>`。不能說是枯燥乏味，~~只能說是無聊至極~~。

## Ch01 / printf 函數

printf 可以精確地格式化輸出。每一次呼叫printf都需要一個格式控制字串（format control string），描述輸出格式。格式控制字串包含了轉換字元、旗標、欄位寬度、精度和文字字元。這些元素與百分號（%）組合而成轉換規範（conversion specification）。

printf 函數具有以下格式化能力：

- 將浮點數四捨五入到指定的小數位數
- 將輸出右對齊和左對齊
- 以指數格式表示浮點數
- 以八進制和十六進制格式表示無號整數
- 以固定的欄位寬度和精度顯示所有類型的資料

printf 函數的格式為:

```c
printf(格式控制字串, 其他參數);
```

格式控制字串描述輸出格式，而其他參數（可選）對應於格式控制字串中的每個轉換規範。每個轉換規範以百分號開頭，並以轉換字元結尾。一個格式控制字串可以包含多個轉換規範。

### 1. 整數轉換規範

整數是不含小數點的整數，可以用幾種不同的格式顯示，以下程式範例使用每個整數轉換字元印出一個整數：

```c=
#include <stdio.h>

int main(void)
{
    int x = 459; 

    printf("%d\n", x); /* 459 */
    printf("%i\n", x); /* 459 */
    printf("%o\n", x); /* 713 */
    printf("%u\n", x); /* 459 */
    printf("%x\n", x); /* 1cb */
    printf("%X\n", x); /* 1CB */
    
    /* 僅減號會被印出，加號通常會被隱藏 */
    printf("%d\n", -x); /* -459 */

    /* 將-455視為無號整數時，其值為4294966841 */
    printf("%u\n", -x); /* 4294966841 */ 
    
    return 0;
}
```

| 格式控制符 | 說明 |
|-------|------|
| `%d`和`%i` | 印出十進制整數 |
| `%o` | 八進制整數 |
| `%u` | 印出無號十進制整數 |
| `%x` | 印出小寫十六進制整數 |
| `%X` | 印出大寫十六進制整數 |

> note：當將負數以`%u`無號整數格式印出時，會得到一個大於等於2^32的值

### 2. 浮點數轉換規範

浮點數包含小數點，程式中通常會使用指數表示法 + 轉換字元`e`和`E`來顯示浮點數，這相當於數學中使用的科學記號法。

例如，150.4582可以用科學記號法表示為1.504582 * 10^2，或用指數記號法表示為1.504582E+02。這表示1.504582乘以10的2次方(E+02)，E代表"指數"。

使用轉換字元`e`、`E`和`f`時，預設顯示小數點右側有6位數的精度。`f`會確保在小數點左側至少有一位數字，而`e`和`E`則會在指數前分別印出小寫e和大寫E，並且在小數點左側只會有一位數字。

轉換字元`g`（或`G`）會根據數值大小，自動選擇使用`e`（或`E`）或`f`格式，且不會印出尾隨的0（如1.234000會被印為1.234）。

說了這麼多密密麻麻的一大堆，還是直接看程式範例比較快：

```c=
#include <stdio.h>

int main(void)
{
    float x = 459.683; 

    printf("%e\n", x); /* 4.596830e+02 */
    printf("%E\n", x); /* 4.596830E+02 */
    printf("%f\n", x); /* 459.683000 */
    printf("%g\n", x); /* 459.683 */ 
    printf("%G\n", x); /* 459.683 */

    /* 根據指數的值,決定使用e或f格式 */
    printf("%g\n", 0.0000875); /* 8.75e-05 */  
    printf("%g\n", 8750000.0); /* 8.75e+06 */
    printf("%g\n", 8.75);      /* 8.75 */
    printf("%g\n", 87.5);      /* 87.5 */

    /* 精度表示最多顯示幾位有效數字(包含整數部分) */
    printf("%g\n", 1234567.0); /* 1.23457e+06 */

    return 0;
}
```
| 格式控制符 | 說明 |
|-------|------|
| `%e`和`%E` | 分別使用小寫e和大寫E印出指數格式的浮點數 |
| `%f` | 以定點表示法印出浮點數，不會四捨五入 |
| `%g`和`%G` | 根據數值大小自動選擇`e`（或`E`）或`f`格式，並移除尾隨的0，其精度控制預設為6 |


## Ch02 / scanf函數

scanf函數可以精確格式化輸入。每一次呼叫 scanf 都需要一個格式控制字串，描述要讀入的資料格式。格式控制字串包含了轉換字元和文字字元。

scanf函數具有以下輸入格式化能力:

- 讀入所有類型的資料
- 從輸入串流讀取特定字元
- 在輸入串流中跳過特定字元

scanf函數的格式為:

```c
scanf(格式控制字串, 其他參數);
```

格式控制字串描述輸入格式，而其他參數是將要存放輸入資料的變數位址。

以下是常用的轉換字元：

| 轉換字元 | 說明 |
|-------|------|
| `%c` | 讀取一個字元 |
| `%d` | 讀取一個十進制整數 |
| `%i` | 讀取一個整數(十進制、八進制或十六進制) |
| `%f` | 讀取一個浮點數 |
| `%lf` | 讀取一個double型浮點數 |
| `%s` | 讀取一個字串 |
| `%x` | 讀取一個十六進制整數 |


## Ch03 / 程式範例

### 範例1：讀取並印出整數

```c=
#include <stdio.h>

int main(void)
{
    int x, y, z;
    
    printf("請輸入三個整數(用空白隔開):");
    scanf("%d %d %d", &x, &y, &z);
    // 使用 %d 轉換字元讀取三個十進制整數到變數x、y和z
    printf("你輸入的三個整數為: %d, %d, %d\n", x, y, z);
    
    return 0;
}
```

### 範例2：計算圓面積

```c=
#include <stdio.h>
#include <math.h>

int main(void)
{
    const double PI = 3.14159265358979323846;
    double radius, area;
    
    printf("請輸入圓的半徑: ");
    scanf("%lf", &radius);
    // 使用 %lf 轉換字元讀取一個double型浮點數到radius變數
    area = PI * radius * radius;
    
    printf("圓的面積為: %.2f\n", area);
    // 使用`%.2f`印出圓面積到小數點後兩位
    return 0;
}
```

### 範例3：讀取並反轉字串

```c=
#include <stdio.h>
#include <string.h>

void reverseString(char str[]);

int main(void)
{
    char str[100];
    
    printf("請輸入一個字串: ");
    scanf("%s", str);
    
    printf("原始字串: %s\n", str);
    reverseString(str);
    printf("反轉後字串: %s\n", str);
    
    return 0;
}

void reverseString(char str[])
{
    int length = strlen(str);
    int i, j;
    char temp;
    
    for (i = 0, j = length - 1; i < j; i++, j--)
    {
        temp = str[i];
        str[i] = str[j];
        str[j] = temp;
    }
    // 不斷交換首尾字元，直到遇到中間
}
```


---
*註: 部分內容截自於網路，此筆記非完全原創。*  
***Latest Updated On：2024.05.31,   
published with the of LICENSE of WTFPL  
Author：Qaron（呱呱）***