C言語を学ぶ上で、データ型を正しく理解し使用することは非常に重要です。
なかでも、整数型に関する signed(符号付き)と unsigned(符号なし)の区別は基本中の基本です。
この記事では、これら二つの型の違い、使い方、そして選択する際のポイントについて説明していきます。
1. Signed 整数型
C言語において、整数型はデフォルトで signed として扱われます。
これは、変数が負の値を保持できることを意味します。
例えば、int 型は通常、-2,147,483,648 ~ 2,147,483,647 までの値を表現することができます。
これらの負の数値は通常、「2の補数形式」で表現されます。
signed 整数は、主に以下のような場面の値の保存に使われます。
・温度
・海面下の深さ
など、マイナスの概念を含むデータを扱う場合に特に有用です。
しかし、負の値を必要としない場合に signed 整数を使用すると、無駄に値の範囲の半分を失うことになります。
その理由は、次の章を読んでいただくとわかります。
2. Unsigned 整数型
一方、unsigned 整数型は負の値を取りません。
これにより、同じビット数で表現できる正の整数の範囲が二倍に広がります。
例えば、unsigned int 型では、0 ~ 4,294,967,295 までの値を表現することができます。
unsigned 型は、主に以下のような場面の値の保存に使われます。
・年齢
・人の数
・配列のサイズ
など、自然に負の数を取り得ない値を扱う場合に最適です。
この型を使用することで、signed整数型よりも、より大きな正の数値を効率的に扱うことができます。
3. Signed と Unsigned の比較
これら二つの型は、数値の範囲だけでなく、演算時の振る舞いも異なります。
特に、オーバーフロー時の振る舞いに注意が必要です。
例えば、unsigned 整数のオーバーフローは、モジュロ演算によって静かに処理されるのに対し、signed 整数のオーバーフローは未定義の振る舞いを引き起こす可能性があります。
オーバーフローとは
オーバーフローとは、計算結果が対象となるデータ型によって表現可能な範囲を超えた場合に発生する現象です。コンピューターでは、全ての数値は特定のビット数で表され、このビット数によって表現できる値の範囲が決定されます。
例えば、8ビットの符号なし整数では、0から255までの数値を表すことができます。このデータ型で256を表現しようとした場合、オーバーフローが発生し、結果は0になります。同様に、8ビットの符号付き整数では、-128から127までの数値を表すことができ、127を超える加算を行うとオーバーフローが発生します。
オーバーフローが発生すると、プログラム内で予期しない数値で演算されることとなり、予期しない動き(バグ)になってしまうので注意が必要です。
プログラミングを行う際には、使用するデータの性質を考慮して、適切な型を選択することが重要です。例えば、負の数を扱わない場合でも、計算の途中でオーバーフローが発生する可能性がある場合は、signed で大きな型を検討することもオーバーフロー回避のひとつです。
(とても大切なことですので、「5. 一般的な間違いと注意点」にも書きます)
4. 実践例
以下は、signed と unsigned を使用したコード例です。
#include <stdio.h>
int main()
{
unsigned int u_val = 3000000000; // 正の大きな数
int s_val = -1; // 負の値
printf("unsigned value: %u\n", u_val);
printf("signed value: %d\n", s_val);
return 0;
}
printfでの出力結果は以下のようになります。
unsigned value: 3000000000 signed value: -1
この例では、unsigned型の変数 u_val は正の大きな数値を扱っており、signed型の変数 s_val は負の値を扱っています。
ここで、unsigned型を使用することで、より大きな正の整数を保持することができています。
次に、unsigned型で定義した変数 u_val を signed型で強制的に型変換(キャストという)すると、以下のようになります。
#include <stdio.h>
int main()
{
unsigned int u_val = 3000000000; // 正の大きな数
int s_val = (unsigned int)u_val; // 強制的に signed 型変換
printf("unsigned value: %u\n", u_val);
printf("signed value: %d\n", s_val);
return 0;
}
printfでの出力結果は以下のようになります。
unsigned value: 3000000000 signed value: -1294967296
signedとunsignedで表現できる整数の範囲が異なるため、型による表現範囲には注意です。
signed型の変数にunsignedでしか表現できない値を入れると上記のように表現できる範囲内でのマイナス値になってしまったりしてプログラムが予期しない動きとなってしまうので、注意しましょう。
5. 一般的な間違いと注意点
型の混在:
signed と unsigned 型を同時に使用すると、予期しない結果を招くことがあります。
特に、演算の結果が自動的に計算される場合、予期しないオーバーフローや値の変化が起こる可能性があります。
混在させる場合は、そのあたりは明示的な型変換を行うことが重要になります。
オーバーフロー:
unsigned 整数型はオーバーフローが発生してもエラーを生成しません。
これは利点にも欠点にもなり得るため、計算がオーバーフローする可能性がある場合は、特に注意が必要です。
負の数は使用せず整数しか使わないとしても、オーバーフローしてもエラー生成ができるように、
わざと大きめのsigned型で整数演算を扱うという手段も有用です。
負の値の扱い:
unsigned 型に負の値を代入しようとすると、予期しない大きな正の値に変換されます。
このような状況を避けるために、負の値を扱う必要がある場合は必ず signed 型を使用するように
してください。
6. まとめ
signed と unsigned の適切な使用は、C言語プログラミングの基本中の基本です。
正しいデータ型を選択することで、プログラムの正確性と効率を向上させることができます。
この記事が、それぞれのデータ型の特性を理解し、より良いプログラムを書くための参考になれば幸いです。
私は専門の学校に通っておらず、知人の勧めで営業職からソフトウェア開発会社へ初心者の状態で入社しました。
当時の面接で正直に「初心者でプログラミングは全くやったことがありませんが勉強して絶対に御社のお役に立てるよう努力します」と面談でお伝えし、とても良い上司の方に拾っていただきました。
そのため、完全「独学」です。
いきなり実践の状態でしたので、会社に迷惑をかけぬよう、通勤時間も帰宅後も
基本的にこの「Cの絵本」を毎日持ち歩き何度も読んでメモしてイメージを植え込みました。
かなりお世話になった本です。
基本がわかると他の言語も「プログラムコード」が自然に読めるようになります。
言語がわからなくても、「何をしようとしているプログラムなのか」というものがつかめるようになります。
是非、読んでみてください。
現在は、新版も出ているようです。
こういった入門本も基本的な考え方を網羅しているので、おすすめです。
是非参考にしてください。
コメント