# C 語言的型別轉換
型別轉換是指一個變數從某種資料型態轉換成另一種資料型態的過程。
在 C 語言中,型別轉換方式可以分為兩種:隱式轉型 (Implicit) 和顯式轉型 (Explicit)。
## 隱式轉型
隱式轉型是由 C 的編譯器根據程式碼自動執行的,因此也叫自動轉型,
通常會在表達式中使用不同類型的數據時發生。
通常會發生在不同型別資料混和運算時或是將資料存入不同型別的變數時。
### 當不同型別資料混和運算時
當運算式中有著多種型別的資料時,編譯器會自動將它們轉換成同一種型別後再進行計算。
但實際上要轉換成哪種型別的?編譯器會選擇運算式中可表示範圍最大的型別。
而我們可以將各種常見的型別依照可表示範圍排列如下:
`char` $\le$ `short` $\le$ `int` $\le$ `long` $\le$ `long long` $\le$ `float` $\le$ `double` $\le$ `long double`
其中字元可以當作表示範圍較小的整數;
浮點數可以表示整數外的其他有理數,因此其可表示範圍比整數還要大;
`unsigned` 關鍵字僅表示最大位元不是符號位元,所以表示範圍跟同類型的 `signed` 型別相同。
例如:
將 `int` 和 `float` 做運算時,編譯器會先將它們轉換成表示範圍較大的 `float` 再運算;
將 `int` 和 `long long` 做運算時,編譯器會先將它們轉換成 `long long` 再運算。
我們再來看以下範例:
```c=
#include <stdio.h>
int main()
{
int a = 1;
double b = 9.0;
printf("%d\n", a + b);
printf("%f\n", a + b);
return 0;
}
```
執行結果:
```c
-1916398120
10.000000
```
我們可以看到第 8 行輸出了一個奇怪的整數,
這是因為將 `int` 型別的 `a` 和 `double` 型別的 `b` 相加時,
編譯器會將兩者都轉換成 `double` 後再相加,所以 `a + b` 的結果是 `double` 型別,
而 `%d` 會讓編譯器將 `double` 型別的資料用整數的編碼方式來解釋,
所以才會得到奇怪的值,~~也就是所謂的垃圾進,垃圾出~~。
而第 9 行使用 `%f` 才會如預期的,輸出正確的值。
或者,我們其實可以在編譯時看到警告:
```
tmp.c: In function ‘main’:
tmp.c:8:14: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘double’ [-Wformat=]
8 | printf("%d\n", a + b);
| ~^ ~~~~~
| | |
| int double
| %f
```
意思是 `a + b` 的型別是 `double`,而不是 `%d` 所預期的 `int` 型別。
雖然程式可以執行,但結果可能不符預期,所以建議將 `%d` 改成 `%f`。
### 當資料存入不同型別的變數時
在我們把資料存入不同型別的變數時,編譯器會先自動的將資料轉換成跟變數相同的型別,例如:
```c=
int a = 1;
double b = a;
printf("%d %f\n", a, b); // 輸出: 1 1.000000
```
在第 2 行,將 `int` 型別的資料存入 `double` 型別的變數 `b` 時,
C 會自動把 `int` 資料先轉換成 `double` 型別的資料,再把它存進 `b`。
要注意的是,如果將浮點數轉換成整數時,會無條件捨去小數部分的值,例如:
```c
double a = 0.0, b = 1.414, c = -8.6;
int p = a, q = b, r = c;
printf("a = %.3f, b = %.3f, c = %.3f\n", a, b, c);
printf("p = %d, q = %d, r = %d\n", p, q, r);
```
執行結果:
```
a = 0.000, b = 1.414, c = -8.600
p = 0, q = 1, r = -8
```
## 顯式轉型
顯式轉型與隱式轉型相反,是明確的告訴編譯器要將某種數值強制轉換成某種資料型態,通常會在需要更精確的控制資料型別時使用,~~或是想用就用~~。
它的用法是將想要轉型的資料前面加上一對小括號 (`()`) 在裡面寫下目標的資料型態,例如:
```c
printf("%f\n", (double)0xC8763); // 輸出: 821091.000000
printf("%c\n", (char)107.8682); // 輸出: k
```
範例:
將兩個整數相除,並保留小數點後的結果。
```c
int a, b;
scanf("%d %d", &a, &b);
double ans = (double)a / b;
// 當然我們也可以乘以 1.0 來讓編譯器將 a, b 轉成 double
// => 1.0 * a / b;
printf("%.3f\n", ans);
```