
C言語基礎|整数拡張と通常の算術型変換
小さい型のまま計算してるつもりが、いつの間にか型が変わってる!―Cの型変換ルールを“表でスッキリ”整理しよう。
Cの計算は「型変換」が勝手に入るのがふつう
C言語の式は、見た目どおりに計算してくれることも多いんですが、実はその裏で
- 小さい整数型が自動で大きい型に持ち上げられる(整数拡張)
- さらに、演算のために左右の型をそろえる(通常の算術型変換)
という“型変換の2段階”がほぼ毎回起きます。
これを知らないと、たとえば
- unsigned と signed を混ぜたら結果が巨大な数になる。
- char や short を計算したら、結果が int として扱われていた。
- 浮動小数点を int に直したら小数部が消えた。
みたいな「えっ?」が起こります。ここを、表と図でしっかり整えていきますね。

まずは全体像:式の型はこう決まる
図:式が評価されるときの型変換の流れ

整数拡張:小さい整数型はまず int か unsigned int になる
何が対象?
「int 型もしくは unsigned int 型を使ってよい式の中」では、次のような型がまず拡張されます。
- _Bool
- char / signed char / unsigned char
- short / unsigned short
- int より小さい整数型の式
- int / unsigned int のビットフィールド(幅が int 未満など)
どう拡張される?
ポイントはこれだけです。
- 元の型の全ての値が int に収まるなら int
- 収まらないなら unsigned int
整数拡張の決め方(超重要)
| 元の型(例) | 判定 | 拡張後 |
|---|---|---|
| signed char / short | だいたい int に収まる | int |
| unsigned char(0〜255) | 多くの処理系で int に収まる | int |
| unsigned short(0〜65535) | int が 16bit で収まらない場合がある | unsigned int になり得る |
| _Bool | 0 or 1 なので必ず int に収まる | int |
表の説明
多くの現代環境では int が 32bit なので、unsigned char や unsigned short もだいたい int になります。
ただし「処理系による」ので、“最終的な型はルールで判定する”のが安全です。
通常の算術型変換:演算の前に左右を同じ型にそろえる
算術型(整数型・浮動小数点型)をオペランドに取る多くの演算子(+ - * / % など)は、同じ手順で型をそろえてから計算します。これが 通常の算術型変換 です。
まずは浮動小数点が優先
共通の実数型を決めるルール
| 条件 | 行われる変換 | 結果の型 |
|---|---|---|
| どちらかが long double | もう片方も long double へ | long double |
| それ以外で、どちらかが double | もう片方も double へ | double |
| それ以外で、どちらかが float | もう片方も float へ | float |
| それ以外 | 次に整数どうしのルールへ | (整数の結果型) |
表の説明
混ざったら “より広い浮動小数点型” に寄せます。
たとえば int + double は int を double に変換してから計算し、結果も double です。
整数どうしの通常の算術型変換:signed と unsigned が混ざると要注意
浮動小数点が絡まないなら、ここからは整数どうしの調整です。
このとき、まず 整数拡張 を両方に行ってから、次の規則でそろえます。
整数どうしをそろえる規則(実務で一番事故るところ)
| ルール | 条件 | 変換のしかた | ありがちな現象 |
|---|---|---|---|
| 1 | 2つが同じ型 | 変換しない | そのまま |
| 2 | 両方とも signed または両方とも unsigned | 変換順位が低い方を高い方へ | short と long なら short が long へ |
| 3 | unsigned 側の順位が signed 側以上 | signed を unsigned の型へ | 負数が巨大な正数に化ける |
| 4 | signed の型が unsigned の全値を表現できる | unsigned を signed の型へ | たとえば long が unsigned int を全部表せる環境 |
| 5 | それ以外 | 両方を signed 側に対応する unsigned 型へ | 結局 unsigned 寄りになる |
まず押さえる:整数変換順位って何?
整数変換順位は、ざっくり言うと「型の強さ(優先度)」です。
- 2つの整数型を同じ型にそろえるとき
→ 順位が低い方が高い方へ寄せられやすい - signed と unsigned が混ざるとき
→ 順位に加えて「符号の有無」ルールが絡んで、挙動がややこしくなる
ここを表でクリアにします。
整数変換順位の詳しい表(よく使う型を全部まとめ)
この表は「どの型がどの段にいるか」を整理したものです。
同じ段の中では、signed と unsigned は“順位としては同格”扱いになります(挙動は符号ルールで変わるけど、まず順位は同じ段だよ、という意味)。
整数変換順位(段ごと)
| 変換順位(低 → 高) | 型(signed 系) | 型(unsigned 系) | よくある用途・特徴 |
|---|---|---|---|
| 1 | _Bool | (なし) | 0/1 の真偽。式の中では整数拡張で int 側へ上がりやすい |
| 2 | signed char | unsigned char | 1バイト整数。文字コードや小さい数値の保持 |
| 3 | short | unsigned short | 2バイトのことが多い。古い環境や省メモリで登場 |
| 4 | int | unsigned int | いちばん基本。算術演算の中心になりやすい |
| 5 | long | unsigned long | ポインタサイズに近いことが多い環境もある |
| 6 | long long | unsigned long long | 64bit 整数の代表格。大きいカウンタやIDなど |
この表の説明
- 「順位」は型変換の“力関係”を表します。
- 同じ段(例:int と unsigned int)は順位同じ。
- ただし signed/unsigned が混ざるときは、順位だけでなく符号ルールで結果が変わります。
図:整数変換順位の階段

図の説明
上に行くほど“強い型”。弱い型は強い型にそろえられやすいです。
値の型変換:代入やキャストで起きる“値の変化”も押さえよう
ここは「式の型」ではなく「値そのものがどう変わるか」です。
符号付き整数型と符号無し整数型の変換
整数型 → 整数型の変換結果
| 変換 | 変換先で表現可能? | 結果 |
|---|---|---|
| 整数 → 別の整数 | 可能 | 値は変わらない |
| 整数 → unsigned(表現不可) | 不可 | 最大値+1 で割った剰余(いわゆる modulo) |
| 整数 → signed(表現不可) | 不可 | 処理系定義の値になるか、処理系定義のシグナル生成 |
表の説明
unsigned への変換は “ぐるっと回る” ルールが明確。
signed への変換は環境依存が出るので、危ない変換は避けるのが基本です。
浮動小数点型と整数型の相互変換
浮動小数点 → 整数
- 小数部は切り捨て
- 整数部が変換先の範囲に入らないと 動作は定義されない
浮動小数点 → 整数の要点
| 例 | 変換 | 結果 |
|---|---|---|
| 12.9 → int | 小数部切り捨て | 12 |
| -3.1 → int | 小数部切り捨て | -3 |
| 1e100 → int | 範囲外 | 動作は定義されない |
整数 → 浮動小数点
- 正確に表現できるなら値は変わらない
- 正確に表現できないなら、近い表現可能値へ(どちらに寄せるかは処理系定義)
浮動小数点型どうしの変換
float / double / long double の変換
| 変換 | 状況 | 結果 |
|---|---|---|
| float → double / long double | 拡張 | 値は変わらない |
| double → float | 精度が足りない場合あり | 近い表現可能値へ |
| long double → double / float | 範囲や精度で丸めが起きることあり | 近い表現可能値へ |
| 範囲外への変換 | 範囲外 | 動作は定義されない |
よくあるハマりポイントを“短く”表で確認
見た目は普通なのに結果が変わりやすい例
| 例 | 起きていること | 対策 |
|---|---|---|
| 負の signed + unsigned | signed が unsigned に変換されることがある | unsigned を混ぜる前に型をそろえる |
| char や short の計算 | まず int へ整数拡張される | 意図する型で計算するなら明示的にキャスト |
| float を繰り返し制御に使う | 誤差のせいで比較が崩れる | ループは int で回して最後に割る |
| double → int | 小数部切り捨て、範囲外は定義されない | 範囲チェックを入れる |
