---
# System prepended metadata

title: Ch06 函式庫、格式化輸出
tags: [程式教學]

---

# Ch06 函式庫、格式化輸出
> 上一章: [Ch05 for 迴圈](https://hackmd.io/@sunfrancis12/HyL-bCszT) 
> 下一章: [Ch07 陣列](https://hackmd.io/@sunfrancis12/BJ__b0sz6)
> 回目錄: [NTCU程式戰鬥營C講義](https://hackmd.io/@sunfrancis12/ByfdXdjG6)

## 什麼是函式庫

函式庫大家可以想像成DLC或者擴充包的感覺，如果你想要用一些特殊功能的函式，就必須在程式的開頭`#include`，告訴編譯器你要用的函式庫是什麼。
```c=
#include<stdio.h>
#include<math.h>
```

> C 標準函式庫（C standard library，縮寫：libc）是在C語言程式設計中，所有符合標準的標頭檔（head file）的集合，以及常用的函式庫實作程式（如 I/O 輸入輸出和字串控制）
> 
> 每一個函數的名稱與特性會被寫成一個電腦檔案，這個檔案就稱為標頭檔案，但是實際的函數實作是被分存到函式庫檔案裡。

[擷取自維基百科 -C標準函式庫](https://zh.wikipedia.org/zh-tw/C%E6%A8%99%E6%BA%96%E5%87%BD%E5%BC%8F%E5%BA%AB)

## 常用函式庫

### `stdio.h`

`stdio.h`（英語：standard input/output header，標準輸入/輸出頭文件）是C語言為輸入輸出提供的標準庫頭文件

C語言中的所有輸入和輸出都由抽象的字節流來完成，對文件的訪問也通過關聯的輸入或輸出流進行。
> [取自維基百科 -stdio.h](https://zh.wikipedia.org/zh-tw/Stdio.h)

**常用的函式:**
* int `printf`(const char * restrict format, ...);
* int `scanf`(const char * restrict format, ...);

### `stdlib.h`

stdlib.h是C標準函數庫的頭文件，聲明了數值與字符串轉換函數, 偽隨機數生成函數, 動態內存分配函數, 進程控制函數等公共函數。 C++程序應調用等價的cstdlib頭文件.

> [取自維基百科 -stdlib.h](https://zh.wikipedia.org/zh-tw/Stdlib.h)

**常用的函式:**
* atof	把字符串轉換為雙精度浮點數。相當於strtod(s, (char**)NULL).
* atoi	把字符串轉換為整型. 相當於(int)strtol(s, (char**)NULL, 10).
* malloc	動態內存分配，其內容不初始化
* realloc	釋放老的動態內存塊，按照給出的尺寸分配新的動態內存塊，老的內存塊的內容儘量複製到新的內存塊
* free	系統釋放動態分配的內存. 如果是空指針，則無動作發生；如果指針所指不是動態分配的內存塊或者是已釋放的內存塊，則行為是未定義的。
* bsearch	折半搜索.
* qsort	快速排序.
:::info
其實上述的函式現階段應該用不到啦
:::

### `math.h`

`math.h`是C標準函數庫中的頭文件(標頭檔)。提供用於常用高級數學運算的運算函數。
> [取自維基百科 -math.h](https://zh.wikipedia.org/zh-tw/Math.h)

**<math.h>實用的函數:**
* double `pow`(double,double)
    - pow(n,m) = n^m 
* double `sqrt`(double)
    - sqrt(n) = 根號n
* int `abs`(int)
    - abs(n) 對n取絕對值
* double `celi`(double)
    - 對n上取整數
* double `floor`(double)
    - 對n下取整數

實作範例:
```c=
#include<stdio.h>
#include<math.h> //引用math.h函式庫

int main(){
    int a=2,b=4;

    printf("a的b次方: a^b = %lf\n",pow(a,b));
    printf("b開根號 = %lf\n",sqrt(b));
    printf("a跟b絕對值: |a-b| = %d",abs(a-b));
}
```
輸出結果:
```c=
a的b次方: a^b = 16.000000
b開根號 = 2.000000
a跟b絕對值: |a-b| = 0
```

## 格式化輸出

每次在輸出許多資料時，常常會有資料長度不同導致輸出的格式參差不期
為了增加輸出的可看性，我們需要將輸出的格式統一，而這過程又稱格式化

* 一般輸出:
```c=
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
```
* 格式化輸出
```c=
 1  2  3  4  5  6  7  8  9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
```


## 變數輸出空間規劃

### 變數保留空間

假如我們想要每個整數都有2格的空間，必須在格式化字串的`%`後面填上你要的格數
```c=
printf("%2d",num); //每個整數都有2格，且靠右對齊
```
範例程式:
```c=
#include<stdio.h>

int main(){
    int num=51;

    printf("無格式化的結果:\n");

    int count=1;
    while(count<=num){
        printf("%d ",count);
        if(count%10==0) printf("\n");
        count++;
    }

    printf("\n有格式化的結果:\n");

    count=1;
    while(count<=num){
        printf("%2d ",count);
        if(count%10==0) printf("\n");
        count++;
    }
}
```
輸出結果:
```c=
無格式化的結果:
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51
有格式化的結果:
 1  2  3  4  5  6  7  8  9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51
```

### 變數靠右、靠左對齊

只要在`%`後面加上正負號就可以控制變數靠左還是靠右輸出
* 靠右: `+`
* 靠左: `-`
```c=
printf("%-3d ",count); //為每個變數保留3個空間，而且靠左對齊
```
範例程式:
```c=
#include<stdio.h>

int main(){
    int num=31;

    printf("靠右對齊:\n");

    int count=1;
    while(count<=num){
        printf("%3d ",count);
        if(count%10==0) printf("\n");
        count++;
    }

    printf("\n靠左對齊:\n");

    count=1;
    while(count<=num){
        printf("%-3d ",count);
        if(count%10==0) printf("\n");
        count++;
    }
}
```
輸入內容:
```c=
靠右對齊:
  1   2   3   4   5   6   7   8   9  10
 11  12  13  14  15  16  17  18  19  20
 21  22  23  24  25  26  27  28  29  30
 31
靠左對齊:
1   2   3   4   5   6   7   8   9   10
11  12  13  14  15  16  17  18  19  20
21  22  23  24  25  26  27  28  29  30
31
```

### 空格補0

只要在`%`後面加上小數點`.`就可以將變數剩下的空間補上0
```c=
printf("%.3d ",count); //把多出來的空間補上0
也可以寫成:
printf("%0.3d ",count)
```
範例程式:
```c=
#include<stdio.h>

int main(){
    int num=31;

    printf("沒有補0:\n");

    int count=1;
    while(count<=num){
        printf("%3d ",count);
        if(count%10==0) printf("\n");
        count++;
    }

    printf("\n有補0:\n");

    count=1;
    while(count<=num){
        printf("%0.3d ",count);
        if(count%10==0) printf("\n");
        count++;
    }
}
```

輸出結果:
```c=
沒有補0:
  1   2   3   4   5   6   7   8   9  10 
 11  12  13  14  15  16  17  18  19  20
 21  22  23  24  25  26  27  28  29  30
 31 
有補0:
001 002 003 004 005 006 007 008 009 010
011 012 013 014 015 016 017 018 019 020
021 022 023 024 025 026 027 028 029 030
031
```

## 浮點數的小數點處理

如果要固定輸出到小數點後幾位，可以參照以下方式，沒設定的話預設是6位
```c=
printf("%0.3f ",count); //印到小數點後3位
```
範例程式:
```c=
#include<stdio.h>

int main(){

    printf("沒有固定小數點後幾位:\n");

    float count=1;
    for(int i=1;i<=30;i++){
        printf("%f ",count);
        count++;
        if(i%10==0) printf("\n");
    }

    printf("有固定小數點後幾位:\n");

    count=1;
    for(int i=1;i<=30;i++){
        printf("%.3f ",count);
        count++;
        if(i%10==0) printf("\n");
    }
}
```
輸出結果:
```c=
沒有固定小數點後幾位:
1.000000 2.000000 3.000000 4.000000 5.000000 6.000000 7.000000 8.000000 9.000000 10.000000 
11.000000 12.000000 13.000000 14.000000 15.000000 16.000000 17.000000 18.000000 19.000000 20.000000
21.000000 22.000000 23.000000 24.000000 25.000000 26.000000 27.000000 28.000000 29.000000 30.000000
有固定小數點後幾位:
1.000 2.000 3.000 4.000 5.000 6.000 7.000 8.000 9.000 10.000
11.000 12.000 13.000 14.000 15.000 16.000 17.000 18.000 19.000 20.000
21.000 22.000 23.000 24.000 25.000 26.000 27.000 28.000 29.000 30.000
```

### 預留變數空間

如果要限定輸出空間的話也是在`%`後填上你要的格數，如果變數長度超過保留的空間則是會直接輸出
> 切記，小數點`.`也算一格
```c=
printf("%-7.2f ",count); //保留七格，輸出到小數點後2位並且靠左對齊
```
範例程式:
```c=
#include<stdio.h>

int main(){

    printf("保留7格並靠右對齊:\n");

    float count=1;
    for(int i=1;i<=30;i++){
        printf("%7.2f ",count);
        count++;
        if(i%10==0) printf("\n");
    }

    printf("保留7格並靠左對齊:\n");

    count=1;
    for(int i=1;i<=30;i++){
        printf("%-7.2f ",count);
        count++;
        if(i%10==0) printf("\n");
    }
}
```
輸出結果:
```c=
保留7格並靠右對齊:
   1.00    2.00    3.00    4.00    5.00    6.00    7.00    8.00    9.00   10.00 
  11.00   12.00   13.00   14.00   15.00   16.00   17.00   18.00   19.00   20.00
  21.00   22.00   23.00   24.00   25.00   26.00   27.00   28.00   29.00   30.00
保留7格並靠左對齊:
1.00    2.00    3.00    4.00    5.00    6.00    7.00    8.00    9.00    10.00
11.00   12.00   13.00   14.00   15.00   16.00   17.00   18.00   19.00   20.00
21.00   22.00   23.00   24.00   25.00   26.00   27.00   28.00   29.00   30.00
```

## 各進制的輸出
除了最基本的十進制輸出外，我們也可以透過改變格式化字串來決定要用什麼進制輸出
* `%d`: 十進制
* `%o`: 八進制
* `%x`: 十六進制
> [維基百科 -格式化字串](https://zh.wikipedia.org/zh-tw/%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%AD%97%E7%AC%A6%E4%B8%B2)
> 
輸出1001的各進制:
```c=
#include <stdio.h>

int main(){
    printf("十進制的10的轉為10進制的結果: %d\n",1001);
    printf("十進制的10的轉為8進制的結果: %o\n",1001);
    printf("十進制的10的轉為16進制的結果: %x\n",1001);
}
```
輸出結果:
```c=
十進制的10的轉為10進制的結果: 1001
十進制的10的轉為8進制的結果: 1751
十進制的10的轉為16進制的結果: 3e9
```
我們也可以透過格式化輸出將空格補0:
```c=
#include <stdio.h>

int main(){
    printf("十進制的10的轉為10進制的結果: %0.5d\n",1001);
    printf("十進制的10的轉為8進制的結果: %0.5o\n",1001);
    printf("十進制的10的轉為16進制的結果: %0.5x\n",1001);
}
```
輸出結果:
```c=
十進制的10的轉為10進制的結果: 01001
十進制的10的轉為8進制的結果: 01751
十進制的10的轉為16進制的結果: 003e9
```


---
> 上一章: [Ch05 for 迴圈](https://hackmd.io/@sunfrancis12/HyL-bCszT) 
> 下一章: [Ch07 陣列](https://hackmd.io/@sunfrancis12/BJ__b0sz6)
> 回目錄: [NTCU程式戰鬥營C講義](https://hackmd.io/@sunfrancis12/ByfdXdjG6)